Merge pull request #230 from krkhan/ctap1-new-apdu-parser

Use new APDU parser in CTAP1 code
This commit is contained in:
Kamran Khan
2020-12-15 01:42:13 -08:00
committed by GitHub
3 changed files with 91 additions and 124 deletions

View File

@@ -16,43 +16,31 @@ use alloc::vec::Vec;
use byteorder::{BigEndian, ByteOrder}; use byteorder::{BigEndian, ByteOrder};
use core::convert::TryFrom; use core::convert::TryFrom;
type ByteArray = &'static [u8];
const APDU_HEADER_LEN: usize = 4; const APDU_HEADER_LEN: usize = 4;
#[cfg_attr(test, derive(Clone, Debug))] #[cfg_attr(test, derive(Clone, Debug))]
#[allow(non_camel_case_types)] #[allow(non_camel_case_types, dead_code)]
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum ApduStatusCode { pub enum ApduStatusCode {
SW_SUCCESS, SW_SUCCESS = 0x90_00,
/// Command successfully executed; 'XX' bytes of data are /// Command successfully executed; 'XX' bytes of data are
/// available and can be requested using GET RESPONSE. /// available and can be requested using GET RESPONSE.
SW_GET_RESPONSE, SW_GET_RESPONSE = 0x61_00,
SW_WRONG_DATA, SW_MEMERR = 0x65_01,
SW_WRONG_LENGTH, SW_WRONG_DATA = 0x6a_80,
SW_COND_USE_NOT_SATISFIED, SW_WRONG_LENGTH = 0x67_00,
SW_FILE_NOT_FOUND, SW_COND_USE_NOT_SATISFIED = 0x69_85,
SW_INCORRECT_P1P2, SW_FILE_NOT_FOUND = 0x6a_82,
SW_INCORRECT_P1P2 = 0x6a_86,
/// Instruction code not supported or invalid /// Instruction code not supported or invalid
SW_INS_INVALID, SW_INS_INVALID = 0x6d_00,
SW_CLA_INVALID, SW_CLA_INVALID = 0x6e_00,
SW_INTERNAL_EXCEPTION, SW_INTERNAL_EXCEPTION = 0x6f_00,
} }
impl From<ApduStatusCode> for ByteArray { impl From<ApduStatusCode> for u16 {
fn from(status_code: ApduStatusCode) -> ByteArray { fn from(code: ApduStatusCode) -> Self {
match status_code { code as u16
ApduStatusCode::SW_SUCCESS => b"\x90\x00",
ApduStatusCode::SW_GET_RESPONSE => b"\x61\x00",
ApduStatusCode::SW_WRONG_DATA => b"\x6A\x80",
ApduStatusCode::SW_WRONG_LENGTH => b"\x67\x00",
ApduStatusCode::SW_COND_USE_NOT_SATISFIED => b"\x69\x85",
ApduStatusCode::SW_FILE_NOT_FOUND => b"\x6a\x82",
ApduStatusCode::SW_INCORRECT_P1P2 => b"\x6a\x86",
ApduStatusCode::SW_INS_INVALID => b"\x6d\x00",
ApduStatusCode::SW_CLA_INVALID => b"\x6e\x00",
ApduStatusCode::SW_INTERNAL_EXCEPTION => b"\x6f\x00",
}
} }
} }
@@ -67,10 +55,10 @@ pub enum ApduInstructions {
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Default, PartialEq)] #[derive(Default, PartialEq)]
pub struct ApduHeader { pub struct ApduHeader {
cla: u8, pub cla: u8,
ins: u8, pub ins: u8,
p1: u8, pub p1: u8,
p2: u8, pub p2: u8,
} }
impl From<&[u8; APDU_HEADER_LEN]> for ApduHeader { impl From<&[u8; APDU_HEADER_LEN]> for ApduHeader {
@@ -110,11 +98,11 @@ pub enum ApduType {
#[allow(dead_code)] #[allow(dead_code)]
#[derive(PartialEq)] #[derive(PartialEq)]
pub struct APDU { pub struct APDU {
header: ApduHeader, pub header: ApduHeader,
lc: u16, pub lc: u16,
data: Vec<u8>, pub data: Vec<u8>,
le: u32, pub le: u32,
case_type: ApduType, pub case_type: ApduType,
} }
impl TryFrom<&[u8]> for APDU { impl TryFrom<&[u8]> for APDU {
@@ -182,12 +170,19 @@ impl TryFrom<&[u8]> for APDU {
} }
if payload.len() > 2 { if payload.len() > 2 {
// Lc is possibly three-bytes long // Lc is possibly three-bytes long
let extended_apdu_lc: usize = BigEndian::read_u16(&payload[1..]) as usize; let extended_apdu_lc = BigEndian::read_u16(&payload[1..3]) as usize;
let extended_apdu_le_len: usize = if payload.len() > extended_apdu_lc { if payload.len() < extended_apdu_lc + 3 {
payload.len() - extended_apdu_lc - 3 return Err(ApduStatusCode::SW_WRONG_LENGTH);
} else { }
0
}; let extended_apdu_le_len: usize = payload
.len()
.checked_sub(extended_apdu_lc + 3)
.ok_or(ApduStatusCode::SW_WRONG_LENGTH)?;
if extended_apdu_le_len > 3 {
return Err(ApduStatusCode::SW_WRONG_LENGTH);
}
if byte_0 == 0 && extended_apdu_le_len <= 3 { if byte_0 == 0 && extended_apdu_le_len <= 3 {
// If first byte is zero AND the next two bytes can be parsed as a big-endian // If first byte is zero AND the next two bytes can be parsed as a big-endian
// length that covers the rest of the block (plus few additional bytes for Le), we // length that covers the rest of the block (plus few additional bytes for Le), we

View File

@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
use super::apdu::{ApduStatusCode, APDU};
use super::hid::ChannelID; use super::hid::ChannelID;
use super::status_code::Ctap2StatusCode; use super::status_code::Ctap2StatusCode;
use super::CtapState; use super::CtapState;
@@ -22,49 +23,12 @@ use core::convert::TryFrom;
use crypto::rng256::Rng256; use crypto::rng256::Rng256;
use libtock_drivers::timer::ClockValue; use libtock_drivers::timer::ClockValue;
// For now, they're the same thing with apdu.rs containing the authoritative definition
pub type Ctap1StatusCode = ApduStatusCode;
// The specification referenced in this file is at: // The specification referenced in this file is at:
// https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.pdf // https://fidoalliance.org/specs/fido-u2f-v1.2-ps-20170411/fido-u2f-raw-message-formats-v1.2-ps-20170411.pdf
// status codes specification (version 20170411) section 3.3
#[allow(non_camel_case_types)]
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Debug, PartialEq))]
pub enum Ctap1StatusCode {
SW_NO_ERROR = 0x9000,
SW_CONDITIONS_NOT_SATISFIED = 0x6985,
SW_WRONG_DATA = 0x6A80,
SW_WRONG_LENGTH = 0x6700,
SW_CLA_NOT_SUPPORTED = 0x6E00,
SW_INS_NOT_SUPPORTED = 0x6D00,
SW_MEMERR = 0x6501,
SW_COMMAND_ABORTED = 0x6F00,
SW_VENDOR_KEY_HANDLE_TOO_LONG = 0xF000,
}
impl TryFrom<u16> for Ctap1StatusCode {
type Error = ();
fn try_from(value: u16) -> Result<Ctap1StatusCode, ()> {
match value {
0x9000 => Ok(Ctap1StatusCode::SW_NO_ERROR),
0x6985 => Ok(Ctap1StatusCode::SW_CONDITIONS_NOT_SATISFIED),
0x6A80 => Ok(Ctap1StatusCode::SW_WRONG_DATA),
0x6700 => Ok(Ctap1StatusCode::SW_WRONG_LENGTH),
0x6E00 => Ok(Ctap1StatusCode::SW_CLA_NOT_SUPPORTED),
0x6D00 => Ok(Ctap1StatusCode::SW_INS_NOT_SUPPORTED),
0x6501 => Ok(Ctap1StatusCode::SW_MEMERR),
0x6F00 => Ok(Ctap1StatusCode::SW_COMMAND_ABORTED),
0xF000 => Ok(Ctap1StatusCode::SW_VENDOR_KEY_HANDLE_TOO_LONG),
_ => Err(()),
}
}
}
impl Into<u16> for Ctap1StatusCode {
fn into(self) -> u16 {
self as u16
}
}
#[cfg_attr(any(test, feature = "debug_ctap"), derive(Clone, Debug))] #[cfg_attr(any(test, feature = "debug_ctap"), derive(Clone, Debug))]
#[derive(PartialEq)] #[derive(PartialEq)]
pub enum Ctap1Flags { pub enum Ctap1Flags {
@@ -118,11 +82,14 @@ impl TryFrom<&[u8]> for U2fCommand {
type Error = Ctap1StatusCode; type Error = Ctap1StatusCode;
fn try_from(message: &[u8]) -> Result<Self, Ctap1StatusCode> { fn try_from(message: &[u8]) -> Result<Self, Ctap1StatusCode> {
if message.len() < Ctap1Command::APDU_HEADER_LEN as usize { let apdu: APDU = match APDU::try_from(message) {
return Err(Ctap1StatusCode::SW_WRONG_DATA); Ok(apdu) => apdu,
Err(apdu_status_code) => {
return Err(Ctap1StatusCode::try_from(apdu_status_code).unwrap())
} }
};
let (apdu, payload) = message.split_at(Ctap1Command::APDU_HEADER_LEN as usize); let lc = apdu.lc as usize;
// ISO7816 APDU Header format. Each cell is 1 byte. Note that the CTAP flavor always // ISO7816 APDU Header format. Each cell is 1 byte. Note that the CTAP flavor always
// encodes the length on 3 bytes and doesn't use the field "Le" (Length Expected). // encodes the length on 3 bytes and doesn't use the field "Le" (Length Expected).
@@ -131,19 +98,17 @@ impl TryFrom<&[u8]> for U2fCommand {
// +-----+-----+----+----+-----+-----+-----+ // +-----+-----+----+----+-----+-----+-----+
// | CLA | INS | P1 | P2 | Lc1 | Lc2 | Lc3 | // | CLA | INS | P1 | P2 | Lc1 | Lc2 | Lc3 |
// +-----+-----+----+----+-----+-----+-----+ // +-----+-----+----+----+-----+-----+-----+
if apdu[0] != Ctap1Command::CTAP1_CLA { if apdu.header.cla != Ctap1Command::CTAP1_CLA {
return Err(Ctap1StatusCode::SW_CLA_NOT_SUPPORTED); return Err(Ctap1StatusCode::SW_CLA_INVALID);
} }
let lc = (((apdu[4] as u32) << 16) | ((apdu[5] as u32) << 8) | (apdu[6] as u32)) as usize;
// Since there is always request data, the expected length is either omitted or // Since there is always request data, the expected length is either omitted or
// encoded in 2 bytes. // encoded in 2 bytes.
if lc != payload.len() && lc + 2 != payload.len() { if lc != apdu.data.len() && lc + 2 != apdu.data.len() {
return Err(Ctap1StatusCode::SW_WRONG_LENGTH); return Err(Ctap1StatusCode::SW_WRONG_LENGTH);
} }
match apdu[1] { match apdu.header.ins {
// U2F raw message format specification, Section 4.1 // U2F raw message format specification, Section 4.1
// +-----------------+-------------------+ // +-----------------+-------------------+
// + Challenge (32B) | Application (32B) | // + Challenge (32B) | Application (32B) |
@@ -153,8 +118,8 @@ impl TryFrom<&[u8]> for U2fCommand {
return Err(Ctap1StatusCode::SW_WRONG_LENGTH); return Err(Ctap1StatusCode::SW_WRONG_LENGTH);
} }
Ok(Self::Register { Ok(Self::Register {
challenge: *array_ref!(payload, 0, 32), challenge: *array_ref!(apdu.data, 0, 32),
application: *array_ref!(payload, 32, 32), application: *array_ref!(apdu.data, 32, 32),
}) })
} }
@@ -166,15 +131,15 @@ impl TryFrom<&[u8]> for U2fCommand {
if lc < 65 { if lc < 65 {
return Err(Ctap1StatusCode::SW_WRONG_LENGTH); return Err(Ctap1StatusCode::SW_WRONG_LENGTH);
} }
let handle_length = payload[64] as usize; let handle_length = apdu.data[64] as usize;
if lc != 65 + handle_length { if lc != 65 + handle_length {
return Err(Ctap1StatusCode::SW_WRONG_LENGTH); return Err(Ctap1StatusCode::SW_WRONG_LENGTH);
} }
let flag = Ctap1Flags::try_from(apdu[2])?; let flag = Ctap1Flags::try_from(apdu.header.p1)?;
Ok(Self::Authenticate { Ok(Self::Authenticate {
challenge: *array_ref!(payload, 0, 32), challenge: *array_ref!(apdu.data, 0, 32),
application: *array_ref!(payload, 32, 32), application: *array_ref!(apdu.data, 32, 32),
key_handle: payload[65..lc].to_vec(), key_handle: apdu.data[65..].to_vec(),
flags: flag, flags: flag,
}) })
} }
@@ -190,11 +155,11 @@ impl TryFrom<&[u8]> for U2fCommand {
// For Vendor specific command. // For Vendor specific command.
Ctap1Command::VENDOR_SPECIFIC_FIRST..=Ctap1Command::VENDOR_SPECIFIC_LAST => { Ctap1Command::VENDOR_SPECIFIC_FIRST..=Ctap1Command::VENDOR_SPECIFIC_LAST => {
Ok(Self::VendorSpecific { Ok(Self::VendorSpecific {
payload: payload.to_vec(), payload: apdu.data.to_vec(),
}) })
} }
_ => Err(Ctap1StatusCode::SW_INS_NOT_SUPPORTED), _ => Err(Ctap1StatusCode::SW_INS_INVALID),
} }
} }
} }
@@ -202,8 +167,6 @@ impl TryFrom<&[u8]> for U2fCommand {
pub struct Ctap1Command {} pub struct Ctap1Command {}
impl Ctap1Command { impl Ctap1Command {
const APDU_HEADER_LEN: u32 = 7; // CLA + INS + P1 + P2 + LC1-3
const CTAP1_CLA: u8 = 0; const CTAP1_CLA: u8 = 0;
// This byte is used in Register, but only serves backwards compatibility. // This byte is used in Register, but only serves backwards compatibility.
const LEGACY_BYTE: u8 = 0x05; const LEGACY_BYTE: u8 = 0x05;
@@ -234,7 +197,7 @@ impl Ctap1Command {
application, application,
} => { } => {
if !ctap_state.u2f_up_state.consume_up(clock_value) { if !ctap_state.u2f_up_state.consume_up(clock_value) {
return Err(Ctap1StatusCode::SW_CONDITIONS_NOT_SATISFIED); return Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED);
} }
Ctap1Command::process_register(challenge, application, ctap_state) Ctap1Command::process_register(challenge, application, ctap_state)
} }
@@ -249,7 +212,7 @@ impl Ctap1Command {
if flags == Ctap1Flags::EnforceUpAndSign if flags == Ctap1Flags::EnforceUpAndSign
&& !ctap_state.u2f_up_state.consume_up(clock_value) && !ctap_state.u2f_up_state.consume_up(clock_value)
{ {
return Err(Ctap1StatusCode::SW_CONDITIONS_NOT_SATISFIED); return Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED);
} }
Ctap1Command::process_authenticate( Ctap1Command::process_authenticate(
challenge, challenge,
@@ -264,7 +227,7 @@ impl Ctap1Command {
U2fCommand::Version => Ok(Vec::<u8>::from(super::U2F_VERSION_STRING)), U2fCommand::Version => Ok(Vec::<u8>::from(super::U2F_VERSION_STRING)),
// TODO: should we return an error instead such as SW_INS_NOT_SUPPORTED? // TODO: should we return an error instead such as SW_INS_NOT_SUPPORTED?
U2fCommand::VendorSpecific { .. } => Err(Ctap1StatusCode::SW_NO_ERROR), U2fCommand::VendorSpecific { .. } => Err(Ctap1StatusCode::SW_SUCCESS),
} }
} }
@@ -292,22 +255,22 @@ impl Ctap1Command {
let pk = sk.genpk(); let pk = sk.genpk();
let key_handle = ctap_state let key_handle = ctap_state
.encrypt_key_handle(sk, &application) .encrypt_key_handle(sk, &application)
.map_err(|_| Ctap1StatusCode::SW_COMMAND_ABORTED)?; .map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?;
if key_handle.len() > 0xFF { if key_handle.len() > 0xFF {
// This is just being defensive with unreachable code. // This is just being defensive with unreachable code.
return Err(Ctap1StatusCode::SW_VENDOR_KEY_HANDLE_TOO_LONG); return Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION);
} }
let certificate = ctap_state let certificate = ctap_state
.persistent_store .persistent_store
.attestation_certificate() .attestation_certificate()
.map_err(|_| Ctap1StatusCode::SW_MEMERR)? .map_err(|_| Ctap1StatusCode::SW_MEMERR)?
.ok_or(Ctap1StatusCode::SW_COMMAND_ABORTED)?; .ok_or(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?;
let private_key = ctap_state let private_key = ctap_state
.persistent_store .persistent_store
.attestation_private_key() .attestation_private_key()
.map_err(|_| Ctap1StatusCode::SW_MEMERR)? .map_err(|_| Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?
.ok_or(Ctap1StatusCode::SW_COMMAND_ABORTED)?; .ok_or(Ctap1StatusCode::SW_INTERNAL_EXCEPTION)?;
let mut response = Vec::with_capacity(105 + key_handle.len() + certificate.len()); let mut response = Vec::with_capacity(105 + key_handle.len() + certificate.len());
response.push(Ctap1Command::LEGACY_BYTE); response.push(Ctap1Command::LEGACY_BYTE);
@@ -362,7 +325,7 @@ impl Ctap1Command {
.map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?; .map_err(|_| Ctap1StatusCode::SW_WRONG_DATA)?;
if let Some(credential_source) = credential_source { if let Some(credential_source) = credential_source {
if flags == Ctap1Flags::CheckOnly { if flags == Ctap1Flags::CheckOnly {
return Err(Ctap1StatusCode::SW_CONDITIONS_NOT_SATISFIED); return Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED);
} }
ctap_state ctap_state
.increment_global_signature_counter() .increment_global_signature_counter()
@@ -448,7 +411,7 @@ mod test {
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE);
// Certificate and private key are missing // Certificate and private key are missing
assert_eq!(response, Err(Ctap1StatusCode::SW_COMMAND_ABORTED)); assert_eq!(response, Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION));
let fake_key = [0x41u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH]; let fake_key = [0x41u8; key_material::ATTESTATION_PRIVATE_KEY_LENGTH];
assert!(ctap_state assert!(ctap_state
@@ -459,7 +422,7 @@ mod test {
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE);
// Certificate is still missing // Certificate is still missing
assert_eq!(response, Err(Ctap1StatusCode::SW_COMMAND_ABORTED)); assert_eq!(response, Err(Ctap1StatusCode::SW_INTERNAL_EXCEPTION));
let fake_cert = [0x99u8; 100]; // Arbitrary length let fake_cert = [0x99u8; 100]; // Arbitrary length
assert!(ctap_state assert!(ctap_state
@@ -513,7 +476,7 @@ mod test {
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
let response = let response =
Ctap1Command::process_command(&message, &mut ctap_state, TIMEOUT_CLOCK_VALUE); Ctap1Command::process_command(&message, &mut ctap_state, TIMEOUT_CLOCK_VALUE);
assert_eq!(response, Err(Ctap1StatusCode::SW_CONDITIONS_NOT_SATISFIED)); assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED));
} }
#[test] #[test]
@@ -529,7 +492,7 @@ mod test {
let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); let message = create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle);
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE);
assert_eq!(response, Err(Ctap1StatusCode::SW_CONDITIONS_NOT_SATISFIED)); assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED));
} }
#[test] #[test]
@@ -559,15 +522,24 @@ mod test {
let rp_id = "example.com"; let rp_id = "example.com";
let application = crypto::sha256::Sha256::hash(rp_id.as_bytes()); let application = crypto::sha256::Sha256::hash(rp_id.as_bytes());
let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap(); let key_handle = ctap_state.encrypt_key_handle(sk, &application).unwrap();
let mut message = let mut message = create_authenticate_message(
create_authenticate_message(&application, Ctap1Flags::CheckOnly, &key_handle); &application,
Ctap1Flags::DontEnforceUpAndSign,
&key_handle,
);
message.push(0x00); message.push(0x00);
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE);
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_LENGTH)); assert!(response.is_ok());
// Two extra zeros are okay, they could encode the expected response length.
message.push(0x00); message.push(0x00);
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE);
assert!(response.is_ok());
message.push(0x00);
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE);
assert!(response.is_ok());
message.push(0x00); message.push(0x00);
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE);
assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_LENGTH)); assert_eq!(response, Err(Ctap1StatusCode::SW_WRONG_LENGTH));
@@ -588,7 +560,7 @@ mod test {
message[0] = 0xEE; message[0] = 0xEE;
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE);
assert_eq!(response, Err(Ctap1StatusCode::SW_CLA_NOT_SUPPORTED)); assert_eq!(response, Err(Ctap1StatusCode::SW_CLA_INVALID));
} }
#[test] #[test]
@@ -606,7 +578,7 @@ mod test {
message[1] = 0xEE; message[1] = 0xEE;
let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE); let response = Ctap1Command::process_command(&message, &mut ctap_state, START_CLOCK_VALUE);
assert_eq!(response, Err(Ctap1StatusCode::SW_INS_NOT_SUPPORTED)); assert_eq!(response, Err(Ctap1StatusCode::SW_INS_INVALID));
} }
#[test] #[test]
@@ -722,6 +694,6 @@ mod test {
ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE); ctap_state.u2f_up_state.grant_up(START_CLOCK_VALUE);
let response = let response =
Ctap1Command::process_command(&message, &mut ctap_state, TIMEOUT_CLOCK_VALUE); Ctap1Command::process_command(&message, &mut ctap_state, TIMEOUT_CLOCK_VALUE);
assert_eq!(response, Err(Ctap1StatusCode::SW_CONDITIONS_NOT_SATISFIED)); assert_eq!(response, Err(Ctap1StatusCode::SW_COND_USE_NOT_SATISFIED));
} }
} }

View File

@@ -417,7 +417,7 @@ impl CtapHid {
#[cfg(feature = "with_ctap1")] #[cfg(feature = "with_ctap1")]
fn ctap1_success_message(cid: ChannelID, payload: &[u8]) -> HidPacketIterator { fn ctap1_success_message(cid: ChannelID, payload: &[u8]) -> HidPacketIterator {
let mut response = payload.to_vec(); let mut response = payload.to_vec();
let code: u16 = ctap1::Ctap1StatusCode::SW_NO_ERROR.into(); let code: u16 = ctap1::Ctap1StatusCode::SW_SUCCESS.into();
response.extend_from_slice(&code.to_be_bytes()); response.extend_from_slice(&code.to_be_bytes());
CtapHid::split_message(Message { CtapHid::split_message(Message {
cid, cid,