1#![forbid(unsafe_code)]
22
23use keyroost_proto::apdu::{build_apdu, build_apdu_get};
24
25pub mod spki;
26pub mod x509;
27pub mod x509_parse;
28
29pub const AID: [u8; 5] = [0xA0, 0x00, 0x00, 0x03, 0x08];
33
34pub const SW_OK: u16 = 0x9000;
36pub const SW_MORE_DATA: u8 = 0x61;
38pub const SW_NOT_FOUND: u16 = 0x6A82;
40
41pub const SW_SECURITY_NOT_SATISFIED: u16 = 0x6982;
43pub const SW_AUTH_BLOCKED: u16 = 0x6983;
46pub const SW_REFERENCE_NOT_FOUND: u16 = 0x6A88;
48
49pub const PIN_REF_APPLICATION: u8 = 0x80;
51pub const PIN_REF_PUK: u8 = 0x81;
53pub const KEY_REF_MANAGEMENT: u8 = 0x9B;
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58#[repr(u8)]
59pub enum Instruction {
60 Select = 0xA4,
62 Verify = 0x20,
64 GetData = 0xCB,
66 GetResponse = 0xC0,
68 GeneralAuthenticate = 0x87,
70 GenerateKeyPair = 0x47,
72 PutData = 0xDB,
74 ChangeReference = 0x24,
76 ResetRetryCounter = 0x2C,
78 GetVersion = 0xFD,
80 GetSerial = 0xF8,
82 GetMetadata = 0xF7,
84 SetManagementKey = 0xFF,
86 SetPinRetries = 0xFA,
88 Reset = 0xFB,
90}
91
92impl Instruction {
93 #[must_use]
94 pub const fn code(self) -> u8 {
95 self as u8
96 }
97}
98
99const INS_SELECT_P1_BY_AID: u8 = 0x04;
100const GET_DATA_P1: u8 = 0x3F;
102const GET_DATA_P2: u8 = 0xFF;
103const TAG_OBJECT_SELECTOR: u8 = 0x5C;
105const TAG_DATA_TEMPLATE: u8 = 0x53;
107const TAG_DYN_AUTH: u8 = 0x7C;
109
110#[derive(Clone, Copy, Debug, PartialEq, Eq)]
113pub enum Slot {
114 Authentication,
116 Signature,
118 KeyManagement,
120 CardAuthentication,
122}
123
124impl Slot {
125 #[must_use]
127 pub const fn key_ref(self) -> u8 {
128 match self {
129 Slot::Authentication => 0x9A,
130 Slot::Signature => 0x9C,
131 Slot::KeyManagement => 0x9D,
132 Slot::CardAuthentication => 0x9E,
133 }
134 }
135
136 #[must_use]
138 pub const fn cert_object_tag(self) -> [u8; 3] {
139 match self {
140 Slot::Authentication => [0x5F, 0xC1, 0x05],
141 Slot::Signature => [0x5F, 0xC1, 0x0A],
142 Slot::KeyManagement => [0x5F, 0xC1, 0x0B],
143 Slot::CardAuthentication => [0x5F, 0xC1, 0x01],
144 }
145 }
146
147 #[must_use]
149 pub const fn label(self) -> &'static str {
150 match self {
151 Slot::Authentication => "authentication (9A)",
152 Slot::Signature => "signature (9C)",
153 Slot::KeyManagement => "key management (9D)",
154 Slot::CardAuthentication => "card authentication (9E)",
155 }
156 }
157
158 #[must_use]
160 pub const fn all() -> [Slot; 4] {
161 [
162 Slot::Authentication,
163 Slot::Signature,
164 Slot::KeyManagement,
165 Slot::CardAuthentication,
166 ]
167 }
168}
169
170pub const OBJECT_CHUID: [u8; 3] = [0x5F, 0xC1, 0x02];
172
173#[derive(Clone, Copy, Debug, PartialEq, Eq)]
176pub enum MgmtAlg {
177 TripleDes,
179 Aes128,
181 Aes192,
183 Aes256,
185}
186
187impl MgmtAlg {
188 #[must_use]
190 pub const fn id(self) -> u8 {
191 match self {
192 MgmtAlg::TripleDes => 0x03,
193 MgmtAlg::Aes128 => 0x08,
194 MgmtAlg::Aes192 => 0x0A,
195 MgmtAlg::Aes256 => 0x0C,
196 }
197 }
198
199 #[must_use]
201 pub const fn from_id(id: u8) -> Option<Self> {
202 match id {
203 0x03 => Some(MgmtAlg::TripleDes),
204 0x08 => Some(MgmtAlg::Aes128),
205 0x0A => Some(MgmtAlg::Aes192),
206 0x0C => Some(MgmtAlg::Aes256),
207 _ => None,
208 }
209 }
210
211 #[must_use]
213 pub const fn block_size(self) -> usize {
214 match self {
215 MgmtAlg::TripleDes => 8,
216 _ => 16,
217 }
218 }
219
220 #[must_use]
222 pub const fn key_len(self) -> usize {
223 match self {
224 MgmtAlg::TripleDes | MgmtAlg::Aes192 => 24,
225 MgmtAlg::Aes128 => 16,
226 MgmtAlg::Aes256 => 32,
227 }
228 }
229
230 #[must_use]
232 pub const fn label(self) -> &'static str {
233 match self {
234 MgmtAlg::TripleDes => "3DES",
235 MgmtAlg::Aes128 => "AES-128",
236 MgmtAlg::Aes192 => "AES-192",
237 MgmtAlg::Aes256 => "AES-256",
238 }
239 }
240}
241
242#[derive(Clone, Copy, Debug, PartialEq, Eq)]
244pub enum KeyAlg {
245 Rsa1024,
246 Rsa2048,
247 Rsa3072,
248 Rsa4096,
249 EccP256,
250 EccP384,
251 Ed25519,
252 X25519,
253}
254
255impl KeyAlg {
256 #[must_use]
258 pub const fn id(self) -> u8 {
259 match self {
260 KeyAlg::Rsa1024 => 0x06,
261 KeyAlg::Rsa2048 => 0x07,
262 KeyAlg::Rsa3072 => 0x05,
263 KeyAlg::Rsa4096 => 0x16,
264 KeyAlg::EccP256 => 0x11,
265 KeyAlg::EccP384 => 0x14,
266 KeyAlg::Ed25519 => 0xE0,
267 KeyAlg::X25519 => 0xE1,
268 }
269 }
270
271 #[must_use]
273 pub const fn from_id(id: u8) -> Option<Self> {
274 match id {
275 0x06 => Some(KeyAlg::Rsa1024),
276 0x07 => Some(KeyAlg::Rsa2048),
277 0x05 => Some(KeyAlg::Rsa3072),
278 0x16 => Some(KeyAlg::Rsa4096),
279 0x11 => Some(KeyAlg::EccP256),
280 0x14 => Some(KeyAlg::EccP384),
281 0xE0 => Some(KeyAlg::Ed25519),
282 0xE1 => Some(KeyAlg::X25519),
283 _ => None,
284 }
285 }
286
287 #[must_use]
289 pub const fn label(self) -> &'static str {
290 match self {
291 KeyAlg::Rsa1024 => "RSA-1024",
292 KeyAlg::Rsa2048 => "RSA-2048",
293 KeyAlg::Rsa3072 => "RSA-3072",
294 KeyAlg::Rsa4096 => "RSA-4096",
295 KeyAlg::EccP256 => "ECC P-256",
296 KeyAlg::EccP384 => "ECC P-384",
297 KeyAlg::Ed25519 => "Ed25519",
298 KeyAlg::X25519 => "X25519",
299 }
300 }
301}
302
303#[derive(Clone, Copy, Debug, PartialEq, Eq)]
305pub enum PinPolicy {
306 Default,
307 Never,
308 Once,
309 Always,
310}
311
312impl PinPolicy {
313 #[must_use]
314 pub const fn id(self) -> u8 {
315 match self {
316 PinPolicy::Default => 0x00,
317 PinPolicy::Never => 0x01,
318 PinPolicy::Once => 0x02,
319 PinPolicy::Always => 0x03,
320 }
321 }
322}
323
324#[derive(Clone, Copy, Debug, PartialEq, Eq)]
326pub enum TouchPolicy {
327 Default,
328 Never,
329 Always,
330 Cached,
331}
332
333impl TouchPolicy {
334 #[must_use]
335 pub const fn id(self) -> u8 {
336 match self {
337 TouchPolicy::Default => 0x00,
338 TouchPolicy::Never => 0x01,
339 TouchPolicy::Always => 0x02,
340 TouchPolicy::Cached => 0x03,
341 }
342 }
343}
344
345#[derive(Debug, Clone, PartialEq, Eq)]
347pub enum PublicKey {
348 Rsa { modulus: Vec<u8>, exponent: Vec<u8> },
350 Ecc { point: Vec<u8> },
353}
354
355#[derive(Debug, Clone, Default, PartialEq, Eq)]
357pub struct Metadata {
358 pub algorithm: Option<u8>,
360 pub is_default: Option<bool>,
362 pub retries: Option<(u8, u8)>,
364 pub policy: Option<(u8, u8)>,
366 pub origin: Option<u8>,
368 pub public_key: Option<Vec<u8>>,
372}
373
374#[derive(Debug, PartialEq, Eq)]
376pub enum ParseError {
377 Truncated,
379 NotDataObject,
381 BadResponse(&'static str),
383 NotAuthTemplate,
385 NotPublicKey,
387}
388
389impl core::fmt::Display for ParseError {
390 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
391 match self {
392 ParseError::Truncated => write!(f, "PIV response truncated"),
393 ParseError::NotDataObject => write!(f, "PIV response is not a 0x53 data object"),
394 ParseError::BadResponse(w) => write!(f, "malformed PIV response: {w}"),
395 ParseError::NotAuthTemplate => {
396 write!(f, "PIV response is not a 0x7C dynamic-auth template")
397 }
398 ParseError::NotPublicKey => {
399 write!(f, "PIV response is not a 0x7F49 public-key template")
400 }
401 }
402 }
403}
404
405impl std::error::Error for ParseError {}
406
407#[must_use]
414pub fn select() -> Vec<u8> {
415 let mut apdu = build_apdu(
416 0x00,
417 Instruction::Select.code(),
418 INS_SELECT_P1_BY_AID,
419 0x00,
420 &AID,
421 );
422 apdu.push(0x00); apdu
424}
425
426#[must_use]
430pub fn get_data(tag: &[u8]) -> Vec<u8> {
431 assert!(tag.len() <= 0x7F, "GET DATA object tag too long");
432 let mut selector = Vec::with_capacity(2 + tag.len());
433 selector.push(TAG_OBJECT_SELECTOR);
434 selector.push(tag.len() as u8);
435 selector.extend_from_slice(tag);
436 let mut apdu = build_apdu(
437 0x00,
438 Instruction::GetData.code(),
439 GET_DATA_P1,
440 GET_DATA_P2,
441 &selector,
442 );
443 apdu.push(0x00); apdu
445}
446
447#[must_use]
450pub fn verify_pin(pin: &[u8]) -> Vec<u8> {
451 build_apdu(
452 0x00,
453 Instruction::Verify.code(),
454 0x00,
455 PIN_REF_APPLICATION,
456 &pad_pin(pin),
457 )
458}
459
460#[must_use]
464pub fn verify_pin_status() -> Vec<u8> {
465 vec![0x00, Instruction::Verify.code(), 0x00, PIN_REF_APPLICATION]
466}
467
468#[must_use]
470pub fn get_version() -> Vec<u8> {
471 build_apdu_get(0x00, Instruction::GetVersion.code(), 0x00, 0x00, 0x00)
472}
473
474#[must_use]
476pub fn get_serial() -> Vec<u8> {
477 build_apdu_get(0x00, Instruction::GetSerial.code(), 0x00, 0x00, 0x00)
478}
479
480#[must_use]
482pub fn get_response() -> Vec<u8> {
483 build_apdu_get(0x00, Instruction::GetResponse.code(), 0x00, 0x00, 0x00)
484}
485
486fn push_ber_len(out: &mut Vec<u8>, len: usize) {
496 assert!(len <= 0xFFFF, "BER-TLV value too large");
497 if len < 0x80 {
498 out.push(len as u8);
499 } else if len <= 0xFF {
500 out.push(0x81);
501 out.push(len as u8);
502 } else {
503 out.push(0x82);
504 out.push((len >> 8) as u8);
505 out.push(len as u8);
506 }
507}
508
509fn push_tlv(out: &mut Vec<u8>, tag: &[u8], value: &[u8]) {
511 out.extend_from_slice(tag);
512 push_ber_len(out, value.len());
513 out.extend_from_slice(value);
514}
515
516fn build_apdu_ext(cla: u8, ins: u8, p1: u8, p2: u8, data: &[u8], le: Option<u16>) -> Vec<u8> {
521 assert!(data.len() <= 0xFFFF, "extended APDU body too large");
522 if data.len() <= 255 && le.map_or(true, |v| v <= 256) {
523 let mut out = Vec::with_capacity(6 + data.len());
525 out.extend_from_slice(&[cla, ins, p1, p2]);
526 if !data.is_empty() {
527 out.push(data.len() as u8);
528 out.extend_from_slice(data);
529 }
530 if let Some(le) = le {
531 out.push(if le == 256 { 0x00 } else { le as u8 });
532 }
533 return out;
534 }
535 let mut out = Vec::with_capacity(9 + data.len());
537 out.extend_from_slice(&[cla, ins, p1, p2, 0x00]);
538 if !data.is_empty() {
539 out.push((data.len() >> 8) as u8);
540 out.push(data.len() as u8);
541 out.extend_from_slice(data);
542 }
543 if let Some(le) = le {
544 out.push((le >> 8) as u8);
546 out.push(le as u8);
547 }
548 out
549}
550
551#[must_use]
559pub fn general_auth_request_witness(alg: MgmtAlg, key_ref: u8) -> Vec<u8> {
560 let data = [TAG_DYN_AUTH, 0x02, 0x80, 0x00];
562 build_apdu_ext(
563 0x00,
564 Instruction::GeneralAuthenticate.code(),
565 alg.id(),
566 key_ref,
567 &data,
568 Some(256),
569 )
570}
571
572#[must_use]
576pub fn general_auth_mutual(
577 alg: MgmtAlg,
578 key_ref: u8,
579 decrypted_witness: &[u8],
580 challenge: &[u8],
581) -> Vec<u8> {
582 let mut inner = Vec::with_capacity(decrypted_witness.len() + challenge.len() + 6);
583 push_tlv(&mut inner, &[0x80], decrypted_witness); push_tlv(&mut inner, &[0x81], challenge); let mut data = Vec::with_capacity(inner.len() + 4);
586 push_tlv(&mut data, &[TAG_DYN_AUTH], &inner);
587 build_apdu_ext(
588 0x00,
589 Instruction::GeneralAuthenticate.code(),
590 alg.id(),
591 key_ref,
592 &data,
593 Some(256),
594 )
595}
596
597#[must_use]
602pub fn general_auth_sign(key_alg: KeyAlg, key_ref: u8, payload: &[u8]) -> Vec<u8> {
603 let mut inner = Vec::with_capacity(payload.len() + 6);
604 inner.extend_from_slice(&[0x82, 0x00]); push_tlv(&mut inner, &[0x81], payload); let mut data = Vec::with_capacity(inner.len() + 4);
607 push_tlv(&mut data, &[TAG_DYN_AUTH], &inner);
608 build_apdu_ext(
609 0x00,
610 Instruction::GeneralAuthenticate.code(),
611 key_alg.id(),
612 key_ref,
613 &data,
614 Some(0), )
616}
617
618#[must_use]
622pub fn generate_key(
623 slot: Slot,
624 alg: KeyAlg,
625 pin_policy: PinPolicy,
626 touch_policy: TouchPolicy,
627) -> Vec<u8> {
628 let mut control = Vec::with_capacity(9);
629 push_tlv(&mut control, &[0x80], &[alg.id()]); if pin_policy != PinPolicy::Default {
631 push_tlv(&mut control, &[0xAA], &[pin_policy.id()]);
632 }
633 if touch_policy != TouchPolicy::Default {
634 push_tlv(&mut control, &[0xAB], &[touch_policy.id()]);
635 }
636 let mut data = Vec::with_capacity(control.len() + 3);
637 push_tlv(&mut data, &[0xAC], &control); build_apdu_ext(
639 0x00,
640 Instruction::GenerateKeyPair.code(),
641 0x00,
642 slot.key_ref(),
643 &data,
644 Some(0),
645 )
646}
647
648#[must_use]
652pub fn put_data(tag: &[u8], value: &[u8]) -> Vec<u8> {
653 let mut data = Vec::with_capacity(tag.len() + value.len() + 8);
654 push_tlv(&mut data, &[TAG_OBJECT_SELECTOR], tag); push_tlv(&mut data, &[TAG_DATA_TEMPLATE], value); build_apdu_ext(
657 0x00,
658 Instruction::PutData.code(),
659 GET_DATA_P1,
660 GET_DATA_P2,
661 &data,
662 None,
663 )
664}
665
666#[must_use]
669pub fn encode_certificate(der: &[u8]) -> Vec<u8> {
670 let mut out = Vec::with_capacity(der.len() + 8);
671 push_tlv(&mut out, &[0x70], der); push_tlv(&mut out, &[0x71], &[0x00]); push_tlv(&mut out, &[0xFE], &[]); out
675}
676
677#[must_use]
680pub fn change_reference(reference: u8, old: &[u8], new: &[u8]) -> Vec<u8> {
681 let mut body = pad_pin(old);
682 body.extend_from_slice(&pad_pin(new));
683 build_apdu(
684 0x00,
685 Instruction::ChangeReference.code(),
686 0x00,
687 reference,
688 &body,
689 )
690}
691
692#[must_use]
695pub fn unblock_pin(puk: &[u8], new_pin: &[u8]) -> Vec<u8> {
696 let mut body = pad_pin(puk);
697 body.extend_from_slice(&pad_pin(new_pin));
698 build_apdu(
699 0x00,
700 Instruction::ResetRetryCounter.code(),
701 0x00,
702 PIN_REF_APPLICATION,
703 &body,
704 )
705}
706
707#[must_use]
711pub fn set_management_key(alg: MgmtAlg, key: &[u8], require_touch: bool) -> Vec<u8> {
712 assert!(key.len() <= 255, "management key too long");
713 let mut body = Vec::with_capacity(3 + key.len());
715 body.push(alg.id());
716 body.push(KEY_REF_MANAGEMENT);
717 body.push(key.len() as u8);
718 body.extend_from_slice(key);
719 build_apdu(
720 0x00,
721 Instruction::SetManagementKey.code(),
722 0xFF,
723 if require_touch { 0xFE } else { 0xFF },
725 &body,
726 )
727}
728
729#[must_use]
732pub fn set_pin_retries(pin_tries: u8, puk_tries: u8) -> Vec<u8> {
733 vec![
734 0x00,
735 Instruction::SetPinRetries.code(),
736 pin_tries,
737 puk_tries,
738 ]
739}
740
741#[must_use]
744pub fn get_metadata(key_ref: u8) -> Vec<u8> {
745 vec![0x00, Instruction::GetMetadata.code(), 0x00, key_ref]
746}
747
748#[must_use]
751pub fn reset() -> Vec<u8> {
752 vec![0x00, Instruction::Reset.code(), 0x00, 0x00]
753}
754
755fn pad_pin(pin: &[u8]) -> Vec<u8> {
758 let mut out = [0xFFu8; 8].to_vec();
759 let n = pin.len().min(8);
760 out[..n].copy_from_slice(&pin[..n]);
761 out
762}
763
764pub fn unwrap_data_object(buf: &[u8]) -> Result<&[u8], ParseError> {
771 if buf.first() != Some(&TAG_DATA_TEMPLATE) {
772 return Err(ParseError::NotDataObject);
773 }
774 let (len, header) = read_ber_len(&buf[1..])?;
775 let start = 1 + header;
776 let end = start.checked_add(len).ok_or(ParseError::Truncated)?;
777 buf.get(start..end).ok_or(ParseError::Truncated)
778}
779
780pub fn parse_version(buf: &[u8]) -> Result<(u8, u8, u8), ParseError> {
782 match buf {
783 [a, b, c] => Ok((*a, *b, *c)),
784 _ => Err(ParseError::BadResponse("version is not 3 bytes")),
785 }
786}
787
788pub fn parse_serial(buf: &[u8]) -> Result<u32, ParseError> {
790 match buf {
791 [a, b, c, d] => Ok(u32::from_be_bytes([*a, *b, *c, *d])),
792 _ => Err(ParseError::BadResponse("serial is not 4 bytes")),
793 }
794}
795
796pub fn parse_general_auth(buf: &[u8], inner_tag: u8) -> Result<&[u8], ParseError> {
800 if buf.first() != Some(&TAG_DYN_AUTH) {
801 return Err(ParseError::NotAuthTemplate);
802 }
803 let (len, header) = read_ber_len(&buf[1..])?;
804 let start = 1 + header;
805 let end = start.checked_add(len).ok_or(ParseError::Truncated)?;
806 let inner = buf.get(start..end).ok_or(ParseError::Truncated)?;
807 find_tlv(inner, inner_tag).ok_or(ParseError::NotAuthTemplate)
808}
809
810pub fn parse_public_key(buf: &[u8]) -> Result<PublicKey, ParseError> {
813 if buf.get(..2) != Some(&[0x7F, 0x49][..]) {
815 return Err(ParseError::NotPublicKey);
816 }
817 let (len, header) = read_ber_len(&buf[2..])?;
818 let start = 2 + header;
819 let end = start.checked_add(len).ok_or(ParseError::Truncated)?;
820 let inner = buf.get(start..end).ok_or(ParseError::Truncated)?;
821 if let Some(point) = find_tlv(inner, 0x86) {
822 return Ok(PublicKey::Ecc {
823 point: point.to_vec(),
824 });
825 }
826 let modulus = find_tlv(inner, 0x81).ok_or(ParseError::NotPublicKey)?;
827 let exponent = find_tlv(inner, 0x82).ok_or(ParseError::NotPublicKey)?;
828 Ok(PublicKey::Rsa {
829 modulus: modulus.to_vec(),
830 exponent: exponent.to_vec(),
831 })
832}
833
834pub fn parse_metadata(buf: &[u8]) -> Result<Metadata, ParseError> {
836 let mut md = Metadata::default();
837 let mut i = 0;
838 while i < buf.len() {
839 let tag = buf[i];
840 let (len, header) = read_ber_len(&buf[i + 1..])?;
841 let vstart = i + 1 + header;
842 let vend = vstart.checked_add(len).ok_or(ParseError::Truncated)?;
843 let value = buf.get(vstart..vend).ok_or(ParseError::Truncated)?;
844 match tag {
845 0x01 => md.algorithm = value.first().copied(),
846 0x02 if value.len() >= 2 => md.policy = Some((value[0], value[1])),
847 0x03 => md.origin = value.first().copied(),
848 0x04 => md.public_key = Some(value.to_vec()),
849 0x05 => md.is_default = value.first().map(|&b| b != 0),
850 0x06 if value.len() >= 2 => md.retries = Some((value[0], value[1])),
851 _ => {}
852 }
853 i = vend;
854 }
855 Ok(md)
856}
857
858#[must_use]
862pub fn find_tlv(buf: &[u8], tag: u8) -> Option<&[u8]> {
863 let mut i = 0;
864 while i < buf.len() {
865 let t = buf[i];
866 let (len, header) = read_ber_len(buf.get(i + 1..)?).ok()?;
867 let vstart = i + 1 + header;
868 let vend = vstart.checked_add(len)?;
869 let value = buf.get(vstart..vend)?;
870 if t == tag {
871 return Some(value);
872 }
873 i = vend;
874 }
875 None
876}
877
878pub fn read_ber_len(buf: &[u8]) -> Result<(usize, usize), ParseError> {
883 let first = *buf.first().ok_or(ParseError::Truncated)?;
884 if first < 0x80 {
885 return Ok((first as usize, 1));
886 }
887 let n = (first & 0x7F) as usize;
888 if n == 0 || n > 2 {
889 return Err(ParseError::BadResponse("unsupported BER length form"));
890 }
891 let bytes = buf.get(1..1 + n).ok_or(ParseError::Truncated)?;
892 let len = bytes.iter().fold(0usize, |acc, &b| (acc << 8) | b as usize);
893 Ok((len, 1 + n))
894}
895
896#[cfg(test)]
897mod tests {
898 use super::*;
899
900 #[test]
901 fn select_bytes() {
902 assert_eq!(
904 select(),
905 vec![0x00, 0xA4, 0x04, 0x00, 0x05, 0xA0, 0x00, 0x00, 0x03, 0x08, 0x00]
906 );
907 }
908
909 #[test]
910 fn get_data_auth_cert_bytes() {
911 assert_eq!(
913 get_data(&Slot::Authentication.cert_object_tag()),
914 vec![0x00, 0xCB, 0x3F, 0xFF, 0x05, 0x5C, 0x03, 0x5F, 0xC1, 0x05, 0x00]
915 );
916 }
917
918 #[test]
919 fn slot_key_refs_and_tags() {
920 assert_eq!(Slot::Authentication.key_ref(), 0x9A);
921 assert_eq!(Slot::Signature.key_ref(), 0x9C);
922 assert_eq!(Slot::KeyManagement.key_ref(), 0x9D);
923 assert_eq!(Slot::CardAuthentication.key_ref(), 0x9E);
924 assert_eq!(Slot::Signature.cert_object_tag(), [0x5F, 0xC1, 0x0A]);
925 assert_eq!(
926 Slot::CardAuthentication.cert_object_tag(),
927 [0x5F, 0xC1, 0x01]
928 );
929 }
930
931 #[test]
932 fn verify_pin_pads_to_eight() {
933 assert_eq!(
935 verify_pin(b"123456"),
936 vec![0x00, 0x20, 0x00, 0x80, 0x08, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF, 0xFF]
937 );
938 }
939
940 #[test]
941 fn verify_status_is_case1() {
942 assert_eq!(verify_pin_status(), vec![0x00, 0x20, 0x00, 0x80]);
943 }
944
945 #[test]
946 fn version_and_serial_apdus() {
947 assert_eq!(get_version(), vec![0x00, 0xFD, 0x00, 0x00, 0x00]);
948 assert_eq!(get_serial(), vec![0x00, 0xF8, 0x00, 0x00, 0x00]);
949 }
950
951 #[test]
952 fn unwrap_short_data_object() {
953 assert_eq!(
955 unwrap_data_object(&[0x53, 0x03, 0xAA, 0xBB, 0xCC]).unwrap(),
956 &[0xAA, 0xBB, 0xCC]
957 );
958 }
959
960 #[test]
961 fn unwrap_long_form_data_object() {
962 let mut buf = vec![0x53, 0x81, 0x80];
964 buf.extend(std::iter::repeat(0x11).take(128));
965 let inner = unwrap_data_object(&buf).unwrap();
966 assert_eq!(inner.len(), 128);
967 assert!(inner.iter().all(|&b| b == 0x11));
968 }
969
970 #[test]
971 fn unwrap_rejects_non_template_and_truncation() {
972 assert_eq!(
973 unwrap_data_object(&[0x70, 0x01, 0x00]),
974 Err(ParseError::NotDataObject)
975 );
976 assert_eq!(
977 unwrap_data_object(&[0x53, 0x05, 0x00]),
978 Err(ParseError::Truncated)
979 );
980 }
981
982 #[test]
983 fn parse_version_and_serial_values() {
984 assert_eq!(parse_version(&[5, 7, 1]).unwrap(), (5, 7, 1));
985 assert!(parse_version(&[5, 7]).is_err());
986 assert_eq!(parse_serial(&[0x02, 0x40, 0x8A, 0x1B]).unwrap(), 0x02408A1B);
987 assert!(parse_serial(&[0x00, 0x01]).is_err());
988 }
989
990 #[test]
991 fn mgmt_alg_round_trips_and_sizes() {
992 for a in [
993 MgmtAlg::TripleDes,
994 MgmtAlg::Aes128,
995 MgmtAlg::Aes192,
996 MgmtAlg::Aes256,
997 ] {
998 assert_eq!(MgmtAlg::from_id(a.id()), Some(a));
999 }
1000 assert_eq!(MgmtAlg::Aes192.id(), 0x0A);
1001 assert_eq!(MgmtAlg::Aes192.block_size(), 16);
1002 assert_eq!(MgmtAlg::Aes192.key_len(), 24);
1003 assert_eq!(MgmtAlg::TripleDes.block_size(), 8);
1004 assert_eq!(MgmtAlg::Aes256.key_len(), 32);
1005 assert_eq!(MgmtAlg::from_id(0x99), None);
1006 }
1007
1008 #[test]
1009 fn key_alg_round_trips() {
1010 for a in [
1011 KeyAlg::Rsa1024,
1012 KeyAlg::Rsa2048,
1013 KeyAlg::Rsa3072,
1014 KeyAlg::Rsa4096,
1015 KeyAlg::EccP256,
1016 KeyAlg::EccP384,
1017 KeyAlg::Ed25519,
1018 KeyAlg::X25519,
1019 ] {
1020 assert_eq!(KeyAlg::from_id(a.id()), Some(a));
1021 }
1022 assert_eq!(KeyAlg::Rsa2048.id(), 0x07);
1023 assert_eq!(KeyAlg::EccP256.id(), 0x11);
1024 }
1025
1026 #[test]
1027 fn witness_request_bytes() {
1028 assert_eq!(
1030 general_auth_request_witness(MgmtAlg::Aes192, KEY_REF_MANAGEMENT),
1031 vec![0x00, 0x87, 0x0A, 0x9B, 0x04, 0x7C, 0x02, 0x80, 0x00, 0x00]
1032 );
1033 }
1034
1035 #[test]
1036 fn mutual_auth_bytes_aes() {
1037 let w = [0xAAu8; 16];
1039 let c = [0xBBu8; 16];
1040 let apdu = general_auth_mutual(MgmtAlg::Aes192, KEY_REF_MANAGEMENT, &w, &c);
1041 assert_eq!(&apdu[..5], &[0x00, 0x87, 0x0A, 0x9B, 0x26]); assert_eq!(&apdu[5..9], &[0x7C, 0x24, 0x80, 0x10]);
1043 assert_eq!(&apdu[9..25], &w);
1044 assert_eq!(&apdu[25..27], &[0x81, 0x10]);
1045 assert_eq!(&apdu[27..43], &c);
1046 assert_eq!(apdu[43], 0x00); }
1048
1049 #[test]
1050 fn generate_key_bytes_default_policy() {
1051 assert_eq!(
1053 generate_key(
1054 Slot::Authentication,
1055 KeyAlg::EccP256,
1056 PinPolicy::Default,
1057 TouchPolicy::Default
1058 ),
1059 vec![0x00, 0x47, 0x00, 0x9A, 0x05, 0xAC, 0x03, 0x80, 0x01, 0x11, 0x00]
1060 );
1061 }
1062
1063 #[test]
1064 fn generate_key_bytes_with_policies() {
1065 assert_eq!(
1067 generate_key(
1068 Slot::Signature,
1069 KeyAlg::Rsa2048,
1070 PinPolicy::Once,
1071 TouchPolicy::Always
1072 ),
1073 vec![
1074 0x00, 0x47, 0x00, 0x9C, 0x0B, 0xAC, 0x09, 0x80, 0x01, 0x07, 0xAA, 0x01, 0x02, 0xAB,
1075 0x01, 0x02, 0x00
1076 ]
1077 );
1078 }
1079
1080 #[test]
1081 fn change_pin_bytes() {
1082 let apdu = change_reference(PIN_REF_APPLICATION, b"123456", b"654321");
1084 assert_eq!(&apdu[..5], &[0x00, 0x24, 0x00, 0x80, 0x10]);
1085 assert_eq!(
1086 &apdu[5..],
1087 &[
1088 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF, 0xFF, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0xFF, 0xFF, ]
1091 );
1092 }
1093
1094 #[test]
1095 fn unblock_pin_bytes() {
1096 let apdu = unblock_pin(b"12345678", b"000000");
1097 assert_eq!(&apdu[..5], &[0x00, 0x2C, 0x00, 0x80, 0x10]);
1098 assert_eq!(&apdu[5..13], b"12345678");
1099 assert_eq!(
1100 &apdu[13..],
1101 &[0x30, 0x30, 0x30, 0x30, 0x30, 0x30, 0xFF, 0xFF]
1102 );
1103 }
1104
1105 #[test]
1106 fn set_management_key_bytes() {
1107 let key = [0x42u8; 24];
1108 let apdu = set_management_key(MgmtAlg::Aes192, &key, false);
1110 assert_eq!(&apdu[..5], &[0x00, 0xFF, 0xFF, 0xFF, 0x1B]);
1111 assert_eq!(&apdu[5..8], &[0x0A, 0x9B, 0x18]);
1112 assert_eq!(&apdu[8..], &key);
1113 assert_eq!(set_management_key(MgmtAlg::Aes192, &key, true)[3], 0xFE);
1115 }
1116
1117 #[test]
1118 fn set_pin_retries_and_reset_and_metadata_bytes() {
1119 assert_eq!(set_pin_retries(5, 3), vec![0x00, 0xFA, 0x05, 0x03]);
1120 assert_eq!(reset(), vec![0x00, 0xFB, 0x00, 0x00]);
1121 assert_eq!(get_metadata(0x9B), vec![0x00, 0xF7, 0x00, 0x9B]);
1122 }
1123
1124 #[test]
1125 fn put_data_short_object() {
1126 let apdu = put_data(&OBJECT_CHUID, &[0xDE, 0xAD]);
1128 assert_eq!(&apdu[..4], &[0x00, 0xDB, 0x3F, 0xFF]);
1130 assert_eq!(apdu[4], 0x09); assert_eq!(
1132 &apdu[5..],
1133 &[0x5C, 0x03, 0x5F, 0xC1, 0x02, 0x53, 0x02, 0xDE, 0xAD]
1134 );
1135 }
1136
1137 #[test]
1138 fn put_data_large_object_uses_extended_apdu() {
1139 let der = vec![0x11u8; 1024];
1141 let value = encode_certificate(&der);
1142 let apdu = put_data(&Slot::Signature.cert_object_tag(), &value);
1143 assert_eq!(&apdu[..5], &[0x00, 0xDB, 0x3F, 0xFF, 0x00]); let lc = ((apdu[5] as usize) << 8) | apdu[6] as usize;
1145 assert_eq!(lc, apdu.len() - 7); }
1147
1148 #[test]
1149 fn encode_certificate_wraps_der() {
1150 let der = [0xAB, 0xCD, 0xEF];
1151 assert_eq!(
1153 encode_certificate(&der),
1154 vec![0x70, 0x03, 0xAB, 0xCD, 0xEF, 0x71, 0x01, 0x00, 0xFE, 0x00]
1155 );
1156 }
1157
1158 #[test]
1159 fn parse_general_auth_extracts_witness() {
1160 let mut buf = vec![0x7C, 0x0A, 0x80, 0x08];
1162 buf.extend_from_slice(&[1, 2, 3, 4, 5, 6, 7, 8]);
1163 assert_eq!(
1164 parse_general_auth(&buf, 0x80).unwrap(),
1165 &[1, 2, 3, 4, 5, 6, 7, 8]
1166 );
1167 assert_eq!(
1169 parse_general_auth(&[0x70, 0x02, 0x80, 0x00], 0x80),
1170 Err(ParseError::NotAuthTemplate)
1171 );
1172 }
1173
1174 #[test]
1175 fn parse_public_key_rsa_and_ecc() {
1176 let mut rsa = vec![
1178 0x7F, 0x49, 0x0B, 0x81, 0x04, 0xAA, 0xBB, 0xCC, 0xDD, 0x82, 0x03,
1179 ];
1180 rsa.extend_from_slice(&[0x01, 0x00, 0x01]);
1181 match parse_public_key(&rsa).unwrap() {
1182 PublicKey::Rsa { modulus, exponent } => {
1183 assert_eq!(modulus, vec![0xAA, 0xBB, 0xCC, 0xDD]);
1184 assert_eq!(exponent, vec![0x01, 0x00, 0x01]);
1185 }
1186 _ => panic!("expected RSA"),
1187 }
1188 let ecc = vec![0x7F, 0x49, 0x06, 0x86, 0x04, 0x04, 0x11, 0x22, 0x33];
1190 match parse_public_key(&ecc).unwrap() {
1191 PublicKey::Ecc { point } => assert_eq!(point, vec![0x04, 0x11, 0x22, 0x33]),
1192 _ => panic!("expected ECC"),
1193 }
1194 }
1195
1196 #[test]
1197 fn parse_metadata_mgmt_and_pin() {
1198 let md =
1200 parse_metadata(&[0x01, 0x01, 0x0A, 0x02, 0x02, 0x00, 0x01, 0x05, 0x01, 0x01]).unwrap();
1201 assert_eq!(md.algorithm, Some(0x0A));
1202 assert_eq!(md.is_default, Some(true));
1203 assert_eq!(md.policy, Some((0x00, 0x01)));
1204 let pin = parse_metadata(&[0x06, 0x02, 0x03, 0x03, 0x05, 0x01, 0x00]).unwrap();
1206 assert_eq!(pin.retries, Some((3, 3)));
1207 assert_eq!(pin.is_default, Some(false));
1208 }
1209
1210 #[test]
1211 fn parse_metadata_origin_and_public_key() {
1212 let md = parse_metadata(&[
1214 0x01, 0x01, 0x11, 0x03, 0x01, 0x01, 0x04, 0x04, 0x86, 0x02, 0xAA, 0xBB,
1215 ])
1216 .unwrap();
1217 assert_eq!(md.origin, Some(1));
1218 assert_eq!(md.public_key, Some(vec![0x86, 0x02, 0xAA, 0xBB]));
1219 }
1220
1221 #[test]
1222 fn parse_metadata_rejects_garbage() {
1223 assert_eq!(parse_metadata(&[0x06]), Err(ParseError::Truncated));
1225 assert_eq!(
1227 parse_metadata(&[0x01, 0x05, 0xAA]),
1228 Err(ParseError::Truncated)
1229 );
1230 assert!(matches!(
1232 parse_metadata(&[0x01, 0x80, 0x00]),
1233 Err(ParseError::BadResponse(_))
1234 ));
1235 }
1236
1237 #[test]
1238 fn general_auth_sign_short_and_extended() {
1239 let apdu = general_auth_sign(KeyAlg::EccP256, 0x9A, &[0xAA, 0xBB, 0xCC, 0xDD]);
1242 assert_eq!(
1243 apdu,
1244 vec![
1245 0x00, 0x87, 0x11, 0x9A, 0x0A, 0x7C, 0x08, 0x82, 0x00, 0x81, 0x04, 0xAA, 0xBB, 0xCC,
1246 0xDD, 0x00,
1247 ]
1248 );
1249 let apdu = general_auth_sign(KeyAlg::Rsa2048, 0x9A, &[0x55; 256]);
1252 assert_eq!(&apdu[..5], &[0x00, 0x87, 0x07, 0x9A, 0x00]);
1254 let lc = ((apdu[5] as usize) << 8) | apdu[6] as usize;
1255 assert_eq!(lc, 4 + 2 + 4 + 256); assert_eq!(&apdu[7..11], &[0x7C, 0x82, 0x01, 0x06]);
1257 assert_eq!(&apdu[apdu.len() - 2..], &[0x00, 0x00]);
1258 assert_eq!(apdu.len(), 7 + lc + 2);
1259 }
1260
1261 #[test]
1262 fn get_response_bytes() {
1263 assert_eq!(get_response(), vec![0x00, 0xC0, 0x00, 0x00, 0x00]);
1264 }
1265
1266 #[test]
1267 fn pad_pin_truncates_and_pads() {
1268 let apdu = verify_pin(b"1234567890");
1271 assert_eq!(&apdu[5..], b"12345678");
1272 let apdu = verify_pin(b"123456");
1273 assert_eq!(
1274 &apdu[5..],
1275 &[0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0xFF, 0xFF]
1276 );
1277 }
1278
1279 #[test]
1280 fn read_ber_len_forms() {
1281 assert_eq!(read_ber_len(&[0x82, 0x01, 0x30]).unwrap(), (0x130, 3));
1283 assert_eq!(read_ber_len(&[0x81, 0xC8]).unwrap(), (0xC8, 2));
1284 assert!(matches!(
1286 read_ber_len(&[0x80]),
1287 Err(ParseError::BadResponse(_))
1288 ));
1289 assert!(matches!(
1290 read_ber_len(&[0x83, 0x01, 0x00, 0x00]),
1291 Err(ParseError::BadResponse(_))
1292 ));
1293 assert_eq!(read_ber_len(&[0x82, 0x01]), Err(ParseError::Truncated));
1295 assert_eq!(read_ber_len(&[]), Err(ParseError::Truncated));
1296 }
1297}