From b5bee1aa2f674a41b49ddc08ea65c22a941af9cd Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Thu, 21 Nov 2019 08:20:08 -0800 Subject: [PATCH] Factor `yubikey` module fns into struct methods Moves all of the functions in the `yubikey` module into an `impl YubiKey` block, and changes the receiver to `&mut self` making them methods. --- src/util.rs | 321 ++-- src/yubikey.rs | 3817 ++++++++++++++++++++++++------------------------ 2 files changed, 2053 insertions(+), 2085 deletions(-) diff --git a/src/util.rs b/src/util.rs index 1f819b8..80a4bf7 100644 --- a/src/util.rs +++ b/src/util.rs @@ -86,7 +86,10 @@ pub static mut CCC_TMPL: &[u8] = &[ pub struct CardId([u8; 16]); /// Get Card ID -pub unsafe fn ykpiv_util_get_cardid(state: &mut YubiKey, cardid: *mut CardId) -> Result<(), Error> { +pub unsafe fn ykpiv_util_get_cardid( + yubikey: &mut YubiKey, + cardid: *mut CardId, +) -> Result<(), Error> { let mut buf = [0u8; CB_OBJ_MAX]; let mut len = buf.len(); let mut res = Ok(()); @@ -95,10 +98,10 @@ pub unsafe fn ykpiv_util_get_cardid(state: &mut YubiKey, cardid: *mut CardId) -> return Err(Error::GenericError); } - _ykpiv_begin_transaction(state)?; + yubikey._ykpiv_begin_transaction()?; - if _ykpiv_ensure_application_selected(state).is_ok() { - res = _ykpiv_fetch_object(state, YKPIV_OBJ_CHUID as i32, buf.as_mut_ptr(), &mut len); + if yubikey._ykpiv_ensure_application_selected().is_ok() { + res = yubikey._ykpiv_fetch_object(YKPIV_OBJ_CHUID as i32, buf.as_mut_ptr(), &mut len); if res.is_ok() { if len != CHUID_TMPL.len() { @@ -113,13 +116,13 @@ pub unsafe fn ykpiv_util_get_cardid(state: &mut YubiKey, cardid: *mut CardId) -> } } - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); res } /// Set Card ID pub unsafe fn ykpiv_util_set_cardid( - state: &mut YubiKey, + yubikey: &mut YubiKey, cardid: *const CardId, ) -> Result<(), Error> { let mut id = [0u8; YKPIV_CARDID_SIZE]; @@ -136,9 +139,9 @@ pub unsafe fn ykpiv_util_set_cardid( ); } - _ykpiv_begin_transaction(state)?; + yubikey._ykpiv_begin_transaction()?; - if _ykpiv_ensure_application_selected(state).is_ok() { + if yubikey._ykpiv_ensure_application_selected().is_ok() { memcpy( buf.as_mut_ptr() as *mut c_void, CHUID_TMPL.as_ptr() as *const c_void, @@ -151,15 +154,11 @@ pub unsafe fn ykpiv_util_set_cardid( id.len(), ); - res = _ykpiv_save_object( - state, - YKPIV_OBJ_CHUID as i32, - buf.as_mut_ptr(), - CHUID_TMPL.len(), - ); + res = + yubikey._ykpiv_save_object(YKPIV_OBJ_CHUID as i32, buf.as_mut_ptr(), CHUID_TMPL.len()); } - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); res } @@ -168,7 +167,7 @@ pub unsafe fn ykpiv_util_set_cardid( pub struct CCCID([u8; 14]); /// Get Cardholder Capability Container (CCC) ID -pub unsafe fn ykpiv_util_get_cccid(state: &mut YubiKey, ccc: *mut CCCID) -> Result<(), Error> { +pub unsafe fn ykpiv_util_get_cccid(yubikey: &mut YubiKey, ccc: *mut CCCID) -> Result<(), Error> { let mut res = Ok(()); let mut buf = [0u8; CB_OBJ_MAX]; let mut len = buf.len(); @@ -177,19 +176,14 @@ pub unsafe fn ykpiv_util_get_cccid(state: &mut YubiKey, ccc: *mut CCCID) -> Resu return Err(Error::GenericError); } - _ykpiv_begin_transaction(state)?; + yubikey._ykpiv_begin_transaction()?; - if _ykpiv_ensure_application_selected(state).is_ok() { - res = _ykpiv_fetch_object( - state, - YKPIV_OBJ_CAPABILITY as i32, - buf.as_mut_ptr(), - &mut len, - ); + if yubikey._ykpiv_ensure_application_selected().is_ok() { + res = yubikey._ykpiv_fetch_object(YKPIV_OBJ_CAPABILITY as i32, buf.as_mut_ptr(), &mut len); if res.is_ok() { if len != CCC_TMPL.len() { - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); return Err(Error::GenericError); } @@ -205,7 +199,7 @@ pub unsafe fn ykpiv_util_get_cccid(state: &mut YubiKey, ccc: *mut CCCID) -> Resu } /// Get Cardholder Capability Container (CCC) ID -pub unsafe fn ykpiv_util_set_cccid(state: &mut YubiKey, ccc: *const CCCID) -> Result<(), Error> { +pub unsafe fn ykpiv_util_set_cccid(yubikey: &mut YubiKey, ccc: *const CCCID) -> Result<(), Error> { let mut res = Ok(()); let mut id = [0u8; 14]; let mut buf = [0u8; 51]; @@ -221,9 +215,9 @@ pub unsafe fn ykpiv_util_set_cccid(state: &mut YubiKey, ccc: *const CCCID) -> Re ); } - _ykpiv_begin_transaction(state)?; + yubikey._ykpiv_begin_transaction()?; - if _ykpiv_ensure_application_selected(state).is_ok() { + if yubikey._ykpiv_ensure_application_selected().is_ok() { len = 51; memcpy( @@ -238,18 +232,18 @@ pub unsafe fn ykpiv_util_set_cccid(state: &mut YubiKey, ccc: *const CCCID) -> Re 14, ); - res = _ykpiv_save_object(state, YKPIV_OBJ_CAPABILITY as i32, buf.as_mut_ptr(), len); + res = yubikey._ykpiv_save_object(YKPIV_OBJ_CAPABILITY as i32, buf.as_mut_ptr(), len); } - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); res } /// Get YubiKey device model -pub unsafe fn ykpiv_util_devicemodel(state: &mut YubiKey) -> u32 { - if state.context == 0 || state.context == -1 { +pub unsafe fn ykpiv_util_devicemodel(yubikey: &mut YubiKey) -> u32 { + if yubikey.context == 0 || yubikey.context == -1 { DEVTYPE_UNKNOWN - } else if state.is_neo { + } else if yubikey.is_neo { DEVTYPE_NEOr3 } else { DEVTYPE_YK4 @@ -301,7 +295,7 @@ pub const SLOTS: [u8; 24] = [ // TODO(tarcieri): fix clippy alignment warnings #[allow(clippy::cast_ptr_alignment)] pub unsafe fn ykpiv_util_list_keys( - state: &mut YubiKey, + yubikey: &mut YubiKey, key_count: *mut u8, data: *mut *mut YkPivKey, data_len: *mut usize, @@ -323,9 +317,9 @@ pub unsafe fn ykpiv_util_list_keys( return Err(Error::GenericError); } - _ykpiv_begin_transaction(state)?; + yubikey._ykpiv_begin_transaction()?; - if _ykpiv_ensure_application_selected(state).is_ok() { + if yubikey._ykpiv_ensure_application_selected().is_ok() { *key_count = 0; *data = ptr::null_mut(); *data_len = 0; @@ -333,7 +327,7 @@ pub unsafe fn ykpiv_util_list_keys( p_data = calloc(CB_PAGE, 1) as (*mut u8); if p_data.is_null() { - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); return Err(Error::MemoryError); } @@ -347,7 +341,7 @@ pub unsafe fn ykpiv_util_list_keys( } cb_buf = buf.len(); - res = _read_certificate(state, SLOTS[i], buf.as_mut_ptr(), &mut cb_buf); + res = _read_certificate(yubikey, SLOTS[i], buf.as_mut_ptr(), &mut cb_buf); if res.is_ok() && (cb_buf > 0) { cb_realloc = if mem::size_of::() @@ -425,13 +419,13 @@ pub unsafe fn ykpiv_util_list_keys( free(p_data as (*mut c_void)); } - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); res } /// Read certificate pub unsafe fn ykpiv_util_read_cert( - state: &mut YubiKey, + yubikey: &mut YubiKey, slot: u8, data: *mut *mut u8, data_len: *mut usize, @@ -444,12 +438,12 @@ pub unsafe fn ykpiv_util_read_cert( return Err(Error::GenericError); } - _ykpiv_begin_transaction(state)?; + yubikey._ykpiv_begin_transaction()?; - if _ykpiv_ensure_application_selected(state).is_ok() { + if yubikey._ykpiv_ensure_application_selected().is_ok() { *data = ptr::null_mut(); *data_len = 0; - res = _read_certificate(state, slot, buf.as_mut_ptr(), &mut cb_buf); + res = _read_certificate(yubikey, slot, buf.as_mut_ptr(), &mut cb_buf); if res.is_ok() { if cb_buf == 0 { *data = ptr::null_mut(); @@ -472,13 +466,13 @@ pub unsafe fn ykpiv_util_read_cert( } } - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); res } /// Write certificate pub unsafe fn ykpiv_util_write_cert( - state: &mut YubiKey, + yubikey: &mut YubiKey, slot: u8, data: *mut u8, data_len: usize, @@ -486,23 +480,23 @@ pub unsafe fn ykpiv_util_write_cert( ) -> Result<(), Error> { let mut res = Ok(()); - _ykpiv_begin_transaction(state)?; + yubikey._ykpiv_begin_transaction()?; - if _ykpiv_ensure_application_selected(state).is_ok() { - res = _write_certificate(state, slot, data, data_len, certinfo); + if yubikey._ykpiv_ensure_application_selected().is_ok() { + res = _write_certificate(yubikey, slot, data, data_len, certinfo); } - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); res } /// Delete certificate -pub unsafe fn ykpiv_util_delete_cert(state: &mut YubiKey, slot: u8) -> Result<(), Error> { - ykpiv_util_write_cert(state, slot, ptr::null_mut(), 0, 0) +pub unsafe fn ykpiv_util_delete_cert(yubikey: &mut YubiKey, slot: u8) -> Result<(), Error> { + ykpiv_util_write_cert(yubikey, slot, ptr::null_mut(), 0, 0) } /// Block PUK -pub unsafe fn ykpiv_util_block_puk(state: &mut YubiKey) -> Result<(), Error> { +pub unsafe fn ykpiv_util_block_puk(yubikey: &mut YubiKey) -> Result<(), Error> { let mut res = Ok(()); let mut puk = [0x30, 0x42, 0x41, 0x44, 0x46, 0x30, 0x30, 0x44]; let mut tries_remaining: i32 = -1; @@ -512,15 +506,15 @@ pub unsafe fn ykpiv_util_block_puk(state: &mut YubiKey) -> Result<(), Error> { let mut cb_item: usize = 0; let mut flags: u8 = 0; - _ykpiv_begin_transaction(state)?; + yubikey._ykpiv_begin_transaction()?; - if _ykpiv_ensure_application_selected(state).is_err() { - let _ = _ykpiv_end_transaction(state); + if yubikey._ykpiv_ensure_application_selected().is_err() { + let _ = yubikey._ykpiv_end_transaction(); return Ok(()); } while tries_remaining != 0 { - res = ykpiv_change_puk(state, puk.as_ptr(), puk.len(), puk.as_ptr(), puk.len()); + res = yubikey.ykpiv_change_puk(puk.as_ptr(), puk.len(), puk.as_ptr(), puk.len()); match res { Ok(()) => puk[0] += 1, @@ -538,7 +532,7 @@ pub unsafe fn ykpiv_util_block_puk(state: &mut YubiKey) -> Result<(), Error> { } } - if _read_metadata(state, TAG_ADMIN, data.as_mut_ptr(), &mut cb_data).is_ok() + if _read_metadata(yubikey, TAG_ADMIN, data.as_mut_ptr(), &mut cb_data).is_ok() && _get_metadata_item( data.as_mut_ptr(), cb_data, @@ -573,14 +567,14 @@ pub unsafe fn ykpiv_util_block_puk(state: &mut YubiKey) -> Result<(), Error> { ) .is_ok() { - if _write_metadata(state, TAG_ADMIN, data.as_mut_ptr(), cb_data).is_err() { + if _write_metadata(yubikey, TAG_ADMIN, data.as_mut_ptr(), cb_data).is_err() { error!("could not write admin metadata"); } } else { error!("could not set admin flags"); } - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); res } @@ -616,7 +610,7 @@ pub struct YkPivContainer { /// Read mscmap pub unsafe fn ykpiv_util_read_mscmap( - state: &mut YubiKey, + yubikey: &mut YubiKey, containers: *mut *mut YkPivContainer, n_containers: *mut usize, ) -> Result<(), Error> { @@ -631,28 +625,23 @@ pub unsafe fn ykpiv_util_read_mscmap( res = Err(Error::GenericError); } - _ykpiv_begin_transaction(state)?; + yubikey._ykpiv_begin_transaction()?; - if _ykpiv_ensure_application_selected(state).is_ok() { + if yubikey._ykpiv_ensure_application_selected().is_ok() { *containers = ptr::null_mut(); *n_containers = 0; - res = _ykpiv_fetch_object( - state, - YKPIV_OBJ_MSCMAP as i32, - buf.as_mut_ptr(), - &mut cb_buf, - ); + res = yubikey._ykpiv_fetch_object(YKPIV_OBJ_MSCMAP as i32, buf.as_mut_ptr(), &mut cb_buf); if res.is_err() { - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); return res; } ptr = buf.as_mut_ptr(); if cb_buf < CB_OBJ_TAG_MIN { - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); return Ok(()); } @@ -661,7 +650,7 @@ pub unsafe fn ykpiv_util_read_mscmap( ptr = ptr.add(_ykpiv_get_length(ptr, &mut len)); if len > cb_buf - (ptr as isize - buf.as_mut_ptr() as isize) as usize { - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); return Ok(()); } @@ -680,8 +669,8 @@ pub unsafe fn ykpiv_util_read_mscmap( } /// Get max object size -unsafe fn _obj_size_max(state: &mut YubiKey) -> usize { - if state.is_neo { +unsafe fn _obj_size_max(yubikey: &mut YubiKey) -> usize { + if yubikey.is_neo { 2048 - 9 } else { CB_OBJ_MAX @@ -690,7 +679,7 @@ unsafe fn _obj_size_max(state: &mut YubiKey) -> usize { /// Write mscmap pub unsafe fn ykpiv_util_write_mscmap( - state: &mut YubiKey, + yubikey: &mut YubiKey, containers: *mut YkPivContainer, n_containers: usize, ) -> Result<(), Error> { @@ -699,24 +688,24 @@ pub unsafe fn ykpiv_util_write_mscmap( let mut offset: usize = 0; let data_len: usize = n_containers.wrapping_mul(mem::size_of::()); - _ykpiv_begin_transaction(state)?; + yubikey._ykpiv_begin_transaction()?; - if _ykpiv_ensure_application_selected(state).is_ok() { + if yubikey._ykpiv_ensure_application_selected().is_ok() { if containers.is_null() || n_containers == 0 { if !containers.is_null() || n_containers != 0 { res = Err(Error::GenericError); } else { - res = _ykpiv_save_object(state, YKPIV_OBJ_MSCMAP as i32, ptr::null_mut(), 0); + res = yubikey._ykpiv_save_object(YKPIV_OBJ_MSCMAP as i32, ptr::null_mut(), 0); } - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); return res; } let req_len = 1 + _ykpiv_set_length(buf.as_mut_ptr(), data_len) + data_len; - if req_len > _obj_size_max(state) { - let _ = _ykpiv_end_transaction(state); + if req_len > _obj_size_max(yubikey) { + let _ = yubikey._ykpiv_end_transaction(); return Err(Error::SizeError); } @@ -729,16 +718,16 @@ pub unsafe fn ykpiv_util_write_mscmap( data_len, ); offset = offset.wrapping_add(data_len); - res = _ykpiv_save_object(state, YKPIV_OBJ_MSCMAP as i32, buf.as_mut_ptr(), offset); + res = yubikey._ykpiv_save_object(YKPIV_OBJ_MSCMAP as i32, buf.as_mut_ptr(), offset); } - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); res } /// Read msroots pub unsafe fn ykpiv_util_read_msroots( - state: &mut YubiKey, + yubikey: &mut YubiKey, data: *mut *mut u8, data_len: *mut usize, ) -> Result<(), Error> { @@ -759,11 +748,11 @@ pub unsafe fn ykpiv_util_read_msroots( return Err(Error::GenericError); } - _ykpiv_begin_transaction(state)?; + yubikey._ykpiv_begin_transaction()?; - res = _ykpiv_ensure_application_selected(state); + res = yubikey._ykpiv_ensure_application_selected(); if res.is_err() { - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); return res; } @@ -771,27 +760,27 @@ pub unsafe fn ykpiv_util_read_msroots( *data_len = 0; // allocate first page - cb_data = _obj_size_max(state); + cb_data = _obj_size_max(yubikey); p_data = calloc(cb_data, 1) as (*mut u8); if p_data.is_null() { - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); return Err(Error::MemoryError); } for object_id in YKPIV_OBJ_MSROOTS1..YKPIV_OBJ_MSROOTS5 { cb_buf = buf.len(); - res = _ykpiv_fetch_object(state, object_id as i32, buf.as_mut_ptr(), &mut cb_buf); + res = yubikey._ykpiv_fetch_object(object_id as i32, buf.as_mut_ptr(), &mut cb_buf); if res.is_err() { - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); return res; } ptr = buf.as_mut_ptr(); if cb_buf < CB_OBJ_TAG_MIN { - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); return Ok(()); } @@ -800,7 +789,7 @@ pub unsafe fn ykpiv_util_read_msroots( if tag != TAG_MSROOTS_MID && (tag != TAG_MSROOTS_END || object_id == YKPIV_OBJ_MSROOTS5) { // the current object doesn't contain a valid part of a msroots file - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); // treat condition as object isn't found return Ok(()); @@ -810,7 +799,7 @@ pub unsafe fn ykpiv_util_read_msroots( // check that decoded length represents object contents if len > cb_buf - (ptr as isize - buf.as_mut_ptr() as isize) as usize { - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); return Ok(()); } @@ -866,13 +855,13 @@ pub unsafe fn ykpiv_util_read_msroots( free(p_data as (*mut c_void)); } - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); res } /// Write msroots pub unsafe fn ykpiv_util_write_msroots( - state: &mut YubiKey, + yubikey: &mut YubiKey, data: *mut u8, data_len: usize, ) -> Result<(), Error> { @@ -882,26 +871,26 @@ pub unsafe fn ykpiv_util_write_msroots( let mut data_offset: usize = 0; let mut data_chunk: usize; let n_objs: usize; - let cb_obj_max = _obj_size_max(state); + let cb_obj_max = _obj_size_max(yubikey); - _ykpiv_begin_transaction(state)?; + yubikey._ykpiv_begin_transaction()?; - if _ykpiv_ensure_application_selected(state).is_ok() { + if yubikey._ykpiv_ensure_application_selected().is_ok() { if data.is_null() || data_len == 0 { if !data.is_null() || data_len != 0 { res = Err(Error::GenericError); } else { - res = _ykpiv_save_object(state, YKPIV_OBJ_MSROOTS1 as i32, ptr::null_mut(), 0); + res = yubikey._ykpiv_save_object(YKPIV_OBJ_MSROOTS1 as i32, ptr::null_mut(), 0); } - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); return res; } n_objs = (data_len / (cb_obj_max - 4)) + 1; if n_objs > 5 { - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); return Err(Error::SizeError); } @@ -931,8 +920,7 @@ pub unsafe fn ykpiv_util_write_msroots( offset = offset.wrapping_add(data_chunk); - res = _ykpiv_save_object( - state, + res = yubikey._ykpiv_save_object( (YKPIV_OBJ_MSROOTS1 + i as u32) as i32, buf.as_mut_ptr(), offset, @@ -946,7 +934,7 @@ pub unsafe fn ykpiv_util_write_msroots( } } - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); res } @@ -964,7 +952,7 @@ const SZ_ROCA_DEFAULT: &str = "was permitted by default, but is not recommended. /// Generate key #[allow(clippy::cognitive_complexity)] pub unsafe fn ykpiv_util_generate_key( - state: &mut YubiKey, + yubikey: &mut YubiKey, slot: u8, algorithm: u8, pin_policy: u8, @@ -991,10 +979,10 @@ pub unsafe fn ykpiv_util_generate_key( let cb_point: usize; let setting_roca: SettingBool; - if ykpiv_util_devicemodel(state) == DEVTYPE_YK4 + if ykpiv_util_devicemodel(yubikey) == DEVTYPE_YK4 && (algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048) - && state.ver.major == 4 - && (state.ver.minor < 3 || state.ver.minor == 3 && (state.ver.patch < 5)) + && yubikey.ver.major == 4 + && (yubikey.ver.minor < 3 || yubikey.ver.minor == 3 && (yubikey.ver.patch < 5)) { setting_roca = setting_get_bool(SZ_SETTING_ROCA, true); @@ -1021,7 +1009,7 @@ pub unsafe fn ykpiv_util_generate_key( (ROCA) and should be replaced. On-chip key generation {} See \ YSA-2017-01 \ for additional information on device replacement and mitigation assistance", - state.serial, psz_msg + yubikey.serial, psz_msg ); if !setting_roca.value { @@ -1056,9 +1044,9 @@ pub unsafe fn ykpiv_util_generate_key( } } - _ykpiv_begin_transaction(state)?; + yubikey._ykpiv_begin_transaction()?; - if _ykpiv_ensure_application_selected(state).is_ok() { + if yubikey._ykpiv_ensure_application_selected().is_ok() { templ[3] = slot; *in_ptr = 0xac; @@ -1088,8 +1076,7 @@ pub unsafe fn ykpiv_util_generate_key( in_ptr = in_ptr.add(3); } - res = _ykpiv_transfer_data( - state, + res = yubikey._ykpiv_transfer_data( templ.as_ptr(), in_data.as_mut_ptr(), in_ptr as isize - in_data.as_mut_ptr() as isize, @@ -1242,7 +1229,7 @@ pub unsafe fn ykpiv_util_generate_key( free(ptr_exp as (*mut c_void)); } - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); res } @@ -1282,7 +1269,7 @@ pub struct YkPivConfig { /// Get config pub unsafe fn ykpiv_util_get_config( - state: &mut YubiKey, + yubikey: &mut YubiKey, config: *mut YkPivConfig, ) -> Result<(), Error> { let mut data = [0u8; YKPIV_OBJ_MAX_SIZE]; @@ -1301,10 +1288,10 @@ pub unsafe fn ykpiv_util_get_config( (*config).pin_last_changed = 0; (*config).mgm_type = YkPivConfigMgmType::YKPIV_CONFIG_MGM_MANUAL; - _ykpiv_begin_transaction(state)?; + yubikey._ykpiv_begin_transaction()?; - if _ykpiv_ensure_application_selected(state).is_ok() { - if _read_metadata(state, TAG_ADMIN, data.as_mut_ptr(), &mut cb_data).is_ok() { + if yubikey._ykpiv_ensure_application_selected().is_ok() { + if _read_metadata(yubikey, TAG_ADMIN, data.as_mut_ptr(), &mut cb_data).is_ok() { if _get_metadata_item( data.as_mut_ptr(), cb_data, @@ -1362,7 +1349,7 @@ pub unsafe fn ykpiv_util_get_config( } cb_data = YKPIV_OBJ_MAX_SIZE; - if _read_metadata(state, TAG_PROTECTED, data.as_mut_ptr(), &mut cb_data).is_ok() { + if _read_metadata(yubikey, TAG_PROTECTED, data.as_mut_ptr(), &mut cb_data).is_ok() { (*config).protected_data_available = true; res = _get_metadata_item( @@ -1395,20 +1382,20 @@ pub unsafe fn ykpiv_util_get_config( } } - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); res } /// Set PIN last changed -pub unsafe fn ykpiv_util_set_pin_last_changed(state: &mut YubiKey) -> Result<(), Error> { +pub unsafe fn ykpiv_util_set_pin_last_changed(yubikey: &mut YubiKey) -> Result<(), Error> { let mut data = [0u8; YKPIV_OBJ_MAX_SIZE]; let mut cb_data = data.len(); let mut res = Ok(()); - _ykpiv_begin_transaction(state)?; + yubikey._ykpiv_begin_transaction()?; - if _ykpiv_ensure_application_selected(state).is_ok() { - if _read_metadata(state, TAG_ADMIN, data.as_mut_ptr(), &mut cb_data).is_err() { + if yubikey._ykpiv_ensure_application_selected().is_ok() { + if _read_metadata(yubikey, TAG_ADMIN, data.as_mut_ptr(), &mut cb_data).is_err() { cb_data = 0; } @@ -1430,13 +1417,13 @@ pub unsafe fn ykpiv_util_set_pin_last_changed(state: &mut YubiKey) -> Result<(), if let Err(e) = &res { error!("could not set pin timestamp, err = {}", e); } else { - res = _write_metadata(state, TAG_ADMIN, data.as_mut_ptr(), cb_data); + res = _write_metadata(yubikey, TAG_ADMIN, data.as_mut_ptr(), cb_data); if let Err(e) = &res { error!("could not write admin data, err = {}", e); } } } - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); res } @@ -1458,7 +1445,7 @@ impl Drop for YkPivMgm { /// Get derived management key (MGM) pub unsafe fn ykpiv_util_get_derived_mgm( - state: &mut YubiKey, + yubikey: &mut YubiKey, pin: &[u8], mgm: &mut YkPivMgm, ) -> Result<(), Error> { @@ -1467,17 +1454,17 @@ pub unsafe fn ykpiv_util_get_derived_mgm( let mut p_item: *mut u8 = ptr::null_mut(); let mut cb_item: usize = 0; - _ykpiv_begin_transaction(state)?; + yubikey._ykpiv_begin_transaction()?; - let mut res = _ykpiv_ensure_application_selected(state); + let mut res = yubikey._ykpiv_ensure_application_selected(); if res.is_err() { - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); return res; } // recover management key - res = _read_metadata(state, TAG_ADMIN, data.as_mut_ptr(), &mut cb_data); + res = _read_metadata(yubikey, TAG_ADMIN, data.as_mut_ptr(), &mut cb_data); if res.is_ok() { res = _get_metadata_item( @@ -1495,7 +1482,7 @@ pub unsafe fn ykpiv_util_get_derived_mgm( cb_item, ); - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); return Err(Error::GenericError); } @@ -1504,13 +1491,13 @@ pub unsafe fn ykpiv_util_get_derived_mgm( } } - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); res } /// Get protected management key (MGM) pub unsafe fn ykpiv_util_get_protected_mgm( - state: &mut YubiKey, + yubikey: &mut YubiKey, mgm: *mut YkPivMgm, ) -> Result<(), Error> { // TODO(tarcieri): replace vec with wrapper type that impls `Zeroize` @@ -1524,10 +1511,10 @@ pub unsafe fn ykpiv_util_get_protected_mgm( return Err(Error::GenericError); } - _ykpiv_begin_transaction(state)?; + yubikey._ykpiv_begin_transaction()?; - if _ykpiv_ensure_application_selected(state).is_ok() { - res = _read_metadata(state, TAG_PROTECTED, data.as_mut_ptr(), &mut cb_data); + if yubikey._ykpiv_ensure_application_selected().is_ok() { + res = _read_metadata(yubikey, TAG_PROTECTED, data.as_mut_ptr(), &mut cb_data); if res.is_err() { error!("could not read protected data, err = {:?}", res); @@ -1558,7 +1545,7 @@ pub unsafe fn ykpiv_util_get_protected_mgm( } } - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); res } @@ -1567,7 +1554,7 @@ pub unsafe fn ykpiv_util_get_protected_mgm( /// To set a generated mgm, pass NULL for mgm, or set mgm.data to all zeroes #[allow(clippy::cognitive_complexity)] pub unsafe fn ykpiv_util_set_protected_mgm( - state: &mut YubiKey, + yubikey: &mut YubiKey, mgm: *mut YkPivMgm, ) -> Result<(), Error> { let mut f_generate: bool; @@ -1597,10 +1584,10 @@ pub unsafe fn ykpiv_util_set_protected_mgm( } } - _ykpiv_begin_transaction(state)?; + yubikey._ykpiv_begin_transaction()?; - if _ykpiv_ensure_application_selected(state).is_err() { - let _ = _ykpiv_end_transaction(state); + if yubikey._ykpiv_ensure_application_selected().is_err() { + let _ = yubikey._ykpiv_end_transaction(); return Ok(()); } @@ -1610,23 +1597,23 @@ pub unsafe fn ykpiv_util_set_protected_mgm( // generate a new mgm key if let Err(e) = getrandom(mgm_key.deref_mut()) { error!("could not generate new mgm, err = {}", e); - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); return Err(Error::RandomnessError); } } - let ykrc = ykpiv_set_mgmkey(state, &mgm_key); + let ykrc = yubikey.ykpiv_set_mgmkey(&mgm_key); if ykrc.is_err() { // if set_mgmkey fails with KeyError, it means the generated key is weak // otherwise, log a warning, since the device mgm key is corrupt or we're in - // a state where we can't set the mgm key + // a yubikey where we can't set the mgm key if Err(Error::KeyError) != ykrc { error!( "could not set new derived mgm key, err = {}", ykrc.as_ref().unwrap_err() ); - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); return ykrc; } } else { @@ -1650,7 +1637,7 @@ pub unsafe fn ykpiv_util_set_protected_mgm( // succeed, regardless of being able to set the metadata // set the new mgm key in protected data - let mut ykrc = _read_metadata(state, TAG_PROTECTED, data.as_mut_ptr(), &mut cb_data); + let mut ykrc = _read_metadata(yubikey, TAG_PROTECTED, data.as_mut_ptr(), &mut cb_data); if ykrc.is_err() { // set current metadata blob size to zero, we'll add to the blank blob @@ -1669,18 +1656,18 @@ pub unsafe fn ykpiv_util_set_protected_mgm( if ykrc.is_err() { error!("could not set protected mgm item, err = {:?}", ykrc); } else { - ykrc = _write_metadata(state, TAG_PROTECTED, data.as_mut_ptr(), cb_data); + ykrc = _write_metadata(yubikey, TAG_PROTECTED, data.as_mut_ptr(), cb_data); if ykrc.is_err() { error!("could not write protected data, err = {:?}", ykrc); - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); return ykrc; } } // set the protected mgm flag in admin data cb_data = YKPIV_OBJ_MAX_SIZE; - ykrc = _read_metadata(state, TAG_ADMIN, data.as_mut_ptr(), &mut cb_data); + ykrc = _read_metadata(yubikey, TAG_ADMIN, data.as_mut_ptr(), &mut cb_data); if ykrc.is_err() { cb_data = 0; @@ -1739,25 +1726,24 @@ pub unsafe fn ykpiv_util_set_protected_mgm( if let Err(e) = &ykrc { error!("could not set admin flags item, err = {}", e); } else { - ykrc = _write_metadata(state, TAG_ADMIN, data.as_mut_ptr(), cb_data); + ykrc = _write_metadata(yubikey, TAG_ADMIN, data.as_mut_ptr(), cb_data); if let Err(e) = ykrc.as_ref() { error!("could not write admin data, err = {}", e); } } - let _ = _ykpiv_end_transaction(state); + let _ = yubikey._ykpiv_end_transaction(); Ok(()) } /// Reset -pub unsafe fn ykpiv_util_reset(state: &mut YubiKey) -> Result<(), Error> { +pub unsafe fn ykpiv_util_reset(yubikey: &mut YubiKey) -> Result<(), Error> { let templ = [0, YKPIV_INS_RESET, 0, 0]; let mut data = [0u8; 255]; let mut recv_len = data.len(); let mut sw: i32 = 0; - let res = ykpiv_transfer_data( - state, + let res = yubikey.ykpiv_transfer_data( templ.as_ptr(), ptr::null(), 0, @@ -1792,7 +1778,7 @@ pub fn ykpiv_util_slot_object(slot: u8) -> u32 { /// Read certificate unsafe fn _read_certificate( - state: &mut YubiKey, + yubikey: &mut YubiKey, slot: u8, buf: *mut u8, buf_len: *mut usize, @@ -1805,7 +1791,7 @@ unsafe fn _read_certificate( return Err(Error::InvalidObject); } - if _ykpiv_fetch_object(state, object_id, buf, buf_len).is_ok() { + if yubikey._ykpiv_fetch_object(object_id, buf, buf_len).is_ok() { ptr = buf; if *buf_len < CB_OBJ_TAG_MIN { @@ -1836,7 +1822,7 @@ unsafe fn _read_certificate( /// Write certificate unsafe fn _write_certificate( - state: &mut YubiKey, + yubikey: &mut YubiKey, slot: u8, data: *mut u8, data_len: usize, @@ -1856,14 +1842,14 @@ unsafe fn _write_certificate( return Err(Error::GenericError); } - return _ykpiv_save_object(state, object_id, ptr::null_mut(), 0); + return yubikey._ykpiv_save_object(object_id, ptr::null_mut(), 0); } req_len = 1 /* cert tag */ + 3 /* compression tag + data*/ + 2 /* lrc */; req_len += _ykpiv_set_length(buf.as_mut_ptr(), data_len); req_len += data_len; - if req_len < data_len || req_len > _obj_size_max(state) { + if req_len < data_len || req_len > _obj_size_max(yubikey) { return Err(Error::SizeError); } @@ -1892,7 +1878,7 @@ unsafe fn _write_certificate( offset += 5; - _ykpiv_save_object(state, object_id, buf.as_mut_ptr(), offset) + yubikey._ykpiv_save_object(object_id, buf.as_mut_ptr(), offset) } /// Get metadata item @@ -2048,7 +2034,7 @@ unsafe fn _set_metadata_item( /// Read metadata unsafe fn _read_metadata( - state: &mut YubiKey, + yubikey: &mut YubiKey, tag: u8, data: *mut u8, pcb_data: *mut usize, @@ -2069,7 +2055,7 @@ unsafe fn _read_metadata( cb_temp = *pcb_data; *pcb_data = 0; - _ykpiv_fetch_object(state, obj_id, data, &mut cb_temp)?; + yubikey._ykpiv_fetch_object(obj_id, data, &mut cb_temp)?; if cb_temp < CB_OBJ_TAG_MIN { return Err(Error::GenericError); @@ -2099,7 +2085,7 @@ unsafe fn _read_metadata( /// Write metadata unsafe fn _write_metadata( - state: &mut YubiKey, + yubikey: &mut YubiKey, tag: u8, data: *mut u8, cb_data: usize, @@ -2107,7 +2093,7 @@ unsafe fn _write_metadata( let mut buf = [0u8; CB_OBJ_MAX]; // XXX REMEMBER TO ZERO let mut p_temp: *mut u8 = buf.as_mut_ptr(); - if cb_data > _obj_size_max(state) - CB_OBJ_TAG_MAX { + if cb_data > _obj_size_max(yubikey) - CB_OBJ_TAG_MAX { return Err(Error::GenericError); } @@ -2118,7 +2104,7 @@ unsafe fn _write_metadata( } as i32; if data.is_null() || cb_data == 0 { - return _ykpiv_save_object(state, obj_id, ptr::null_mut(), 0); + return yubikey._ykpiv_save_object(obj_id, ptr::null_mut(), 0); } *{ @@ -2131,8 +2117,7 @@ unsafe fn _write_metadata( memcpy(p_temp as (*mut c_void), data as (*const c_void), cb_data); p_temp = p_temp.add(cb_data); - _ykpiv_save_object( - state, + yubikey._ykpiv_save_object( obj_id, buf.as_mut_ptr(), ((p_temp as (isize)).wrapping_sub(buf.as_mut_ptr() as (isize)) diff --git a/src/yubikey.rs b/src/yubikey.rs index 06ecf4e..8a650fd 100644 --- a/src/yubikey.rs +++ b/src/yubikey.rs @@ -106,9 +106,15 @@ pub const SCARD_PCI_T1: *const c_void = ptr::null(); // TODO(tarcieri): PLACEHOLDER! pub const SCARD_S_SUCCESS: i32 = 0; -/// Application ID(?) +/// PIV Application ID pub const AID: [u8; 5] = [0xa0, 0x00, 0x00, 0x03, 0x08]; +/// MGMT Application ID +pub const MGMT_AID: [u8; 8] = [0xa0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17]; + +/// Default authentication key +pub const DEFAULT_AUTH_KEY: &[u8; DES_LEN_3DES] = b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08"; + /// YubiKey PIV version #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub struct Version { @@ -134,6 +140,1899 @@ pub struct YubiKey { pub(crate) serial: u32, } +impl YubiKey { + /// Initialize YubiKey client instance + pub fn ykpiv_init() -> Self { + YubiKey { + context: -1, + card: 0, + pin: ptr::null_mut(), + is_neo: false, + ver: Version { + major: 0, + minor: 0, + patch: 0, + }, + serial: 0, + } + } + + /// Cleanup YubiKey session + pub(crate) unsafe fn _ykpiv_done(&mut self, disconnect: bool) -> Result<(), Error> { + if disconnect { + self.ykpiv_disconnect(); + } + + let _ = self._cache_pin(ptr::null(), 0); + Ok(()) + } + + /// Cleanup YubiKey session with external card upon completion + // TODO(tarcieri): make this a `Drop` handler + pub unsafe fn ykpiv_done_with_external_card(&mut self) -> Result<(), Error> { + self._ykpiv_done(false) + } + + /// Cleanup YubiKey session upon completion + pub unsafe fn ykpiv_done(&mut self) -> Result<(), Error> { + self._ykpiv_done(true) + } + + /// Disconnect a YubiKey session + pub unsafe fn ykpiv_disconnect(&mut self) { + if self.card != 0 { + SCardDisconnect(self.card, 0x1); + self.card = 0i32; + } + + if SCardIsValidContext(self.context) == 0x0 { + SCardReleaseContext(self.context); + self.context = -1i32; + } + } + + /// Select application + pub(crate) unsafe fn _ykpiv_select_application(&mut self) -> Result<(), Error> { + let mut data = [0u8; 255]; + let mut recv_len = data.len() as u32; + let mut sw = 0i32; + + let mut apdu = APDU::default(); + apdu.ins = YKPIV_INS_SELECT_APPLICATION; + apdu.p1 = 0x04; + apdu.lc = AID.len() as u8; + + memcpy( + apdu.data.as_mut_ptr() as *mut c_void, + AID.as_ptr() as *const c_void, + AID.len(), + ); + + if let Err(e) = self._send_data(&mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw) { + error!("failed communicating with card: \'{}\'", e); + return Err(e); + } + + if sw != SW_SUCCESS { + error!("failed selecting application: {:04x}", sw); + return Err(Error::GenericError); + } + + // now that the PIV application is selected, retrieve the version + // and serial number. Previously the NEO/YK4 required switching + // to the yk applet to retrieve the serial, YK5 implements this + // as a PIV applet command. Unfortunately, this change requires + // that we retrieve the version number first, so that get_serial + // can determine how to get the serial number, which for the NEO/Yk4 + // will result in another selection of the PIV applet. + + if let Err(e) = self._ykpiv_get_version() { + warn!("failed to retrieve version: \'{}\'", e); + } + + if let Err(e) = self._ykpiv_get_serial(false) { + warn!("failed to retrieve serial number: \'{}\'", e); + } + + Ok(()) + } + + /// Ensure an application is selected (presently noop) + pub(crate) unsafe fn _ykpiv_ensure_application_selected(&mut self) -> Result<(), Error> { + // TODO(tarcieri): ENABLE_APPLICATION_RESELECTION support? + // + // Original C code below: + // + // #if ENABLE_APPLICATION_RESELECTION + // if (NULL == self) { + // return YKPIV_GENERIC_ERROR; + // } + // + // res = ykpiv_verify(self, NULL, 0); + // + // if ((YKPIV_OK != res) && (YKPIV_WRONG_PIN != res)) { + // res = _ykpiv_select_application(self); + // } + // else { + // res = YKPIV_OK; + // } + // + // return res; + // #else + // (void)self; + // return res; + // #endif + + Ok(()) + } + + /// Connect to the YubiKey + pub(crate) unsafe fn _ykpiv_connect( + &mut self, + context: usize, + card: usize, + ) -> Result<(), Error> { + // if the context has changed, and the new context is not valid, return an error + if context != self.context as (usize) && (0x0i32 != SCardIsValidContext(context as (i32))) { + return Err(Error::PcscError); + } + + // if card handle has changed, determine if handle is valid (less efficient, but complete) + if card != self.card as usize { + let mut reader = [0u8; YKPIV_OBJ_MAX_SIZE]; + let mut reader_len = reader.len() as u32; + let mut atr = [0u8; 33]; + let mut atr_len = atr.len() as u32; + + // Cannot set the reader len to NULL. Confirmed in OSX 10.10, + // so we have to retrieve it even though we don't need it. + if SCardStatus( + card as i32, + reader.as_mut_ptr(), + &mut reader_len, + ptr::null_mut(), + ptr::null_mut(), + atr.as_mut_ptr(), + &mut atr_len, + ) != 0 + { + return Err(Error::PcscError); + } + + self.is_neo = (atr_len as usize == YKPIV_ATR_NEO_R3.len() - 1) + && (memcmp( + YKPIV_ATR_NEO_R3.as_ptr() as *const c_void, + atr.as_mut_ptr() as *const c_void, + atr_len as usize, + ) == 0); + } + + self.context = context as i32; + self.card = card as i32; + + // Do not select the applet here, as we need to accommodate commands that are + // sensitive to re-select (custom apdu/auth). All commands that can handle explicit + // selection already check the applet state and select accordingly anyway. + // ykpiv_verify_select is supplied for those who want to select explicitly. + // + // The applet _is_ selected by ykpiv_connect(), but is not selected when bypassing + // it with ykpiv_connect_with_external_card(). + + Ok(()) + } + + /// Connect to an external card + pub unsafe fn ykpiv_connect_with_external_card( + &mut self, + context: usize, + card: usize, + ) -> Result<(), Error> { + self._ykpiv_connect(context, card) + } + + /// Connect to a YubiKey + pub unsafe fn ykpiv_connect(&mut self, wanted: *const c_char) -> Result<(), Error> { + let mut active_protocol: u32 = 0; + let mut reader_buf: [c_char; 2048] = [0; 2048]; + let mut num_readers = reader_buf.len(); + let mut reader_ptr: *mut c_char; + let mut card: i32 = -1i32; + + self.ykpiv_list_readers(reader_buf.as_mut_ptr(), &mut num_readers)?; + + reader_ptr = reader_buf.as_mut_ptr(); + + while *reader_ptr != 0 { + if !wanted.is_null() { + let mut ptr = reader_ptr; + let mut found = false; + + while *ptr != 0 { + if strlen(ptr) < strlen(wanted) { + break; + } + + if strncasecmp(ptr, wanted, strlen(wanted)) == 0 { + found = true; + break; + } + + ptr = ptr.add(1); + } + + if !found { + warn!( + "skipping reader \'{}\' since it doesn\'t match \'{}\'", + CStr::from_ptr(reader_ptr).to_string_lossy(), + CStr::from_ptr(wanted).to_string_lossy() + ); + + continue; + } + } + + info!( + "trying to connect to reader \'{}\'", + CStr::from_ptr(reader_ptr).to_string_lossy() + ); + + let rc = SCardConnect( + self.context, + reader_ptr, + 0x2u32, + 0x2u32, + &mut card, + &mut active_protocol, + ); + + if rc != 0x0 { + error!("SCardConnect failed, rc={}", rc); + } else { + // at this point, card should not equal self->card, + // to allow _ykpiv_connect() to determine device type + if self + ._ykpiv_connect(self.context as (usize), card as (usize)) + .is_err() + { + break; + } + } + + reader_ptr = reader_ptr.add(strlen(reader_ptr) + 1); + } + + if *reader_ptr == b'\0' as c_char { + error!("error: no usable reader found"); + SCardReleaseContext(self.context); + self.context = -1; + return Err(Error::PcscError); + } + + // Select applet. This is done here instead of in _ykpiv_connect() because + // you may not want to select the applet when connecting to a card handle that + // was supplied by an external library. + self._ykpiv_begin_transaction()?; + + let res = self._ykpiv_select_application(); + let _ = self._ykpiv_end_transaction(); + res + } + + /// List readers + pub unsafe fn ykpiv_list_readers( + &mut self, + readers: *mut c_char, + len: *mut usize, + ) -> Result<(), Error> { + let mut num_readers: u32 = 0u32; + let mut rc: i32; + + if SCardIsValidContext(self.context) != 0 { + rc = SCardEstablishContext(0x2, ptr::null(), ptr::null(), &mut self.context); + + if rc != 0 { + error!("error: SCardEstablishContext failed, rc={}", rc); + return Err(Error::PcscError); + } + } + + rc = SCardListReaders(self.context, ptr::null(), ptr::null_mut(), &mut num_readers); + + if rc != 0 { + error!("error: SCardListReaders failed, rc={}", rc); + SCardReleaseContext(self.context); + self.context = -1i32; + return Err(Error::PcscError); + } + + if num_readers as (usize) > *len { + num_readers = *len as (u32); + } else if num_readers as (usize) < *len { + *len = num_readers as (usize); + } + + rc = SCardListReaders(self.context, ptr::null(), readers, &mut num_readers); + + if rc != 0 { + error!("error: SCardListReaders failed, rc={}", rc); + SCardReleaseContext(self.context); + self.context = -1i32; + return Err(Error::PcscError); + } + + *len = num_readers as usize; + Ok(()) + } + + /// Reconnect to a YubiKey + pub(crate) unsafe fn reconnect(&mut self) -> Result<(), Error> { + info!("trying to reconnect to current reader"); + + let mut active_protocol: u32 = 0; + let rc = SCardReconnect(self.card, 0x2u32, 0x2u32, 0x1u32, &mut active_protocol); + + if rc != 0x0 { + error!("SCardReconnect failed, rc={}", rc); + return Err(Error::PcscError); + } + + self._ykpiv_select_application()?; + + if !self.pin.is_null() { + self.ykpiv_verify(self.pin as *const c_char).map(|_| ()) + } else { + Ok(()) + } + } + + /// Begin a transaction + pub(crate) unsafe fn _ykpiv_begin_transaction(&mut self) -> Result<(), Error> { + let mut rc = SCardBeginTransaction(self.card); + + if rc as usize & 0xffff_ffff == 0x8010_0068 { + self.reconnect()?; + rc = SCardBeginTransaction(self.card); + } + + if rc != 0 { + error!("failed to begin pcsc transaction, rc={}", rc); + return Err(Error::PcscError); + } + + Ok(()) + } + + /// End a transaction + pub(crate) unsafe fn _ykpiv_end_transaction(&mut self) -> Result<(), Error> { + let rc = SCardEndTransaction(self.card, 0x0); + + if rc != 0x0 { + error!("failed to end pcsc transaction, rc={}", rc); + return Err(Error::PcscError); + } + + Ok(()) + } + + /// Transfer data + pub(crate) unsafe fn _ykpiv_transfer_data( + &mut self, + templ: *const u8, + in_data: *const u8, + in_len: isize, + mut out_data: *mut u8, + out_len: *mut usize, + sw: *mut i32, + ) -> Result<(), Error> { + let mut _currentBlock; + let mut in_ptr: *const u8 = in_data; + let max_out = *out_len; + let mut res: Result<(), Error>; + let mut recv_len: u32; + + *out_len = 0; + + loop { + let mut this_size: usize = 0xff; + let mut data = [0u8; 261]; + recv_len = data.len() as u32; + + let mut apdu = APDU::default(); + apdu.cla = *templ; + apdu.ins = *templ.offset(1); + apdu.p1 = *templ.offset(2); + apdu.p2 = *templ.offset(3); + + if in_ptr.offset(0xff) < in_data.offset(in_len) { + apdu.cla = 0x10; + } else { + this_size = in_data.offset(in_len) as usize - in_ptr as usize; + } + + trace!("going to send {} bytes in this go", this_size); + + apdu.lc = this_size.try_into().unwrap(); + memcpy( + apdu.data.as_mut_ptr() as *mut c_void, + in_ptr as *const c_void, + this_size, + ); + + res = self._send_data(&mut apdu, data.as_mut_ptr(), &mut recv_len, sw); + + if res.is_err() { + _currentBlock = 24; + break; + } + if *sw != SW_SUCCESS && (*sw >> 8 != 0x61) { + _currentBlock = 24; + break; + } + if (*out_len) + .wrapping_add(recv_len as (usize)) + .wrapping_sub(2usize) + > max_out + { + _currentBlock = 21; + break; + } + + if !out_data.is_null() { + memcpy( + out_data as (*mut c_void), + data.as_mut_ptr() as (*const c_void), + recv_len.wrapping_sub(2u32) as (usize), + ); + out_data = out_data.offset(recv_len.wrapping_sub(2u32) as (isize)); + *out_len = (*out_len).wrapping_add(recv_len.wrapping_sub(2u32) as (usize)); + } + + in_ptr = in_ptr.add(this_size); + + if in_ptr >= in_data.offset(in_len) { + _currentBlock = 10; + break; + } + } + + if _currentBlock == 10 { + loop { + if *sw >> 8 != 0x61 { + _currentBlock = 24; + break; + } + + let mut data = [0u8; 261]; + recv_len = data.len() as u32; + + trace!( + "The card indicates there is {} bytes more data for us.", + *sw & 0xff + ); + + let mut apdu = APDU::default(); + apdu.ins = YKPIV_INS_GET_RESPONSE_APDU; + res = self._send_data(&mut apdu, data.as_mut_ptr(), &mut recv_len, sw); + + if res.is_err() { + _currentBlock = 24; + break; + } + if *sw != SW_SUCCESS && (*sw >> 8 != 0x61) { + _currentBlock = 24; + break; + } + if (*out_len).wrapping_add(recv_len as (usize)).wrapping_sub(2) > max_out { + _currentBlock = 18; + break; + } + + if out_data.is_null() { + continue; + } + + memcpy( + out_data as (*mut c_void), + data.as_mut_ptr() as (*const c_void), + recv_len.wrapping_sub(2) as (usize), + ); + + out_data = out_data.offset(recv_len.wrapping_sub(2) as (isize)); + *out_len = (*out_len).wrapping_add(recv_len.wrapping_sub(2) as (usize)); + } + + if _currentBlock != 24 { + error!( + "Output buffer to small, wanted to write {}, max was {}.", + (*out_len).wrapping_add(recv_len as usize).wrapping_sub(2), + max_out + ); + + return Err(Error::SizeError); + } + } else if _currentBlock == 21 { + error!( + "Output buffer to small, wanted to write {}, max was {}.", + (*out_len).wrapping_add(recv_len as usize).wrapping_sub(2), + max_out + ); + + return Err(Error::SizeError); + } + + res + } + + /// Transfer data + pub unsafe fn ykpiv_transfer_data( + &mut self, + templ: *const u8, + in_data: *const u8, + in_len: isize, + out_data: *mut u8, + out_len: *mut usize, + sw: *mut i32, + ) -> Result<(), Error> { + if let Err(e) = self._ykpiv_begin_transaction() { + *out_len = 0; + return Err(e); + } + + let res = self._ykpiv_transfer_data(templ, in_data, in_len, out_data, out_len, sw); + let _ = self._ykpiv_end_transaction(); + res + } + + /// Send data + pub(crate) unsafe fn _send_data( + &mut self, + apdu: &mut APDU, + data: *mut u8, + recv_len: *mut u32, + sw: *mut i32, + ) -> Result<(), Error> { + let send_len = apdu.lc as u32 + 5; + let mut tmp_len = *recv_len; + + trace!("> {:?}", apdu); + + let rc = SCardTransmit( + self.card, + SCARD_PCI_T1, + apdu.as_mut_ptr() as *mut i8, + send_len, + ptr::null(), + data, + &mut tmp_len, + ); + + if rc != SCARD_S_SUCCESS { + error!("error: SCardTransmit failed, rc={:08x}", rc); + return Err(Error::PcscError); + } + + *recv_len = tmp_len; + trace!("< {:?}", slice::from_raw_parts(data, *recv_len as usize)); + + if *recv_len >= 2 { + *sw = *data.offset((*recv_len).wrapping_sub(2) as (isize)) as (i32) << 8 + | *data.offset((*recv_len).wrapping_sub(1) as (isize)) as (i32); + } else { + *sw = 0; + } + + Ok(()) + } + + /// Authenticate to the card + pub unsafe fn ykpiv_authenticate( + &mut self, + key: Option<&[u8; DES_LEN_3DES]>, + ) -> Result<(), Error> { + let mut data = [0u8; 261]; + let mut recv_len = data.len() as u32; + let mut sw: i32 = 0; + let mut res = Ok(()); + + self._ykpiv_begin_transaction()?; + + if self._ykpiv_ensure_application_selected().is_ok() { + // use the provided mgm key to authenticate; if it hasn't been provided, use default + let mgm_key = DesKey::from_bytes(*key.unwrap_or(DEFAULT_AUTH_KEY)); + + // get a challenge from the card + let mut apdu = APDU::default(); + apdu.ins = YKPIV_INS_AUTHENTICATE; + apdu.p1 = YKPIV_ALGO_3DES; // triple des + apdu.p2 = YKPIV_KEY_CARDMGM; // management key + apdu.lc = 0x04; + apdu.data[0] = 0x7c; + apdu.data[1] = 0x02; + apdu.data[2] = 0x80; + + res = self._send_data(&mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw); + + if res.is_err() { + let _ = self._ykpiv_end_transaction(); + return res; + } else if sw != SW_SUCCESS { + let _ = self._ykpiv_end_transaction(); + return Err(Error::AuthenticationError); + } + + let mut challenge = [0u8; 8]; + challenge.copy_from_slice(&data[4..12]); + + // send a response to the cards challenge and a challenge of our own. + let mut response = [0u8; 8]; + des_decrypt(&mgm_key, &challenge, &mut response); + + recv_len = data.len() as u32; + apdu = APDU::default(); + apdu.ins = YKPIV_INS_AUTHENTICATE; + apdu.p1 = YKPIV_ALGO_3DES; // triple des + apdu.p2 = YKPIV_KEY_CARDMGM; // management key + apdu.data[0] = 0x7c; + apdu.data[1] = 20; // 2 + 8 + 2 +8 + apdu.data[2] = 0x80; + apdu.data[3] = 8; + apdu.data[4..12].copy_from_slice(&response); + apdu.data[12] = 0x81; + apdu.data[13] = 8; + + if getrandom(&mut data[14..22]).is_err() { + error!("failed getting randomness for authentication."); + let _ = self._ykpiv_end_transaction(); + return Err(Error::RandomnessError); + } + challenge.copy_from_slice(&data[14..22]); + + apdu.lc = 22; + + res = self._send_data(&mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw); + + if res.is_err() { + let _ = self._ykpiv_end_transaction(); + return res; + } else if sw != SW_SUCCESS { + let _ = self._ykpiv_end_transaction(); + return Err(Error::AuthenticationError); + } + + // compare the response from the card with our challenge + des_encrypt(&mgm_key, &challenge, &mut response); + + // TODO(tarcieri): constant time comparison! + if response == data[4..12] { + res = Ok(()); + } else { + res = Err(Error::AuthenticationError); + } + } + + let _ = self._ykpiv_end_transaction(); + res + } + + /// Set the management key (MGM) + pub unsafe fn ykpiv_set_mgmkey(&mut self, new_key: &[u8; DES_LEN_3DES]) -> Result<(), Error> { + self.ykpiv_set_mgmkey2(new_key, 0) + } + + /// Set the management key (MGM) + pub(crate) unsafe fn ykpiv_set_mgmkey2( + &mut self, + new_key: &[u8; DES_LEN_3DES], + touch: u8, + ) -> Result<(), Error> { + let mut data = [0u8; 261]; + let mut recv_len = data.len() as u32; + let mut sw: i32 = 0; + let mut res = Ok(()); + let mut apdu = APDU::default(); + + self._ykpiv_begin_transaction()?; + + if self._ykpiv_ensure_application_selected().is_ok() { + if yk_des_is_weak_key(new_key) { + error!( + "won't set new key '{:?}' since it's weak (with odd parity)", + new_key + ); + res = Err(Error::KeyError); + } else { + apdu.ins = YKPIV_INS_SET_MGMKEY; + apdu.p1 = 0xff; + + apdu.p2 = match touch { + 0 => 0xff, + 1 => 0xfe, + _ => { + let _ = self._ykpiv_end_transaction(); + return Err(Error::GenericError); + } + }; + + apdu.lc = DES_LEN_3DES as u8 + 3; + apdu.data[0] = YKPIV_ALGO_3DES; + apdu.data[1] = YKPIV_KEY_CARDMGM; + apdu.data[2] = DES_LEN_3DES as u8; + apdu.data[3..3 + DES_LEN_3DES].copy_from_slice(new_key); + + res = self._send_data(&mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw); + + if res.is_ok() && sw != SW_SUCCESS { + res = Err(Error::GenericError); + } + } + } + + apdu.zeroize(); + let _ = self._ykpiv_end_transaction(); + res + } + + /// Authenticate to the YubiKey + pub(crate) unsafe fn _general_authenticate( + &mut self, + sign_in: *const u8, + in_len: usize, + out: *mut u8, + out_len: *mut usize, + algorithm: u8, + key: u8, + decipher: bool, + ) -> Result<(), Error> { + let mut _currentBlock; + let mut indata = [0u8; 1024]; + let mut dataptr: *mut u8 = indata.as_mut_ptr(); + let mut data = [0u8; 1024]; + let templ = [0, YKPIV_INS_AUTHENTICATE, algorithm, key]; + let mut recv_len = data.len(); + let mut sw: i32 = 0; + let bytes: usize; + let mut len: usize = 0; + + match algorithm { + YKPIV_ALGO_RSA1024 | YKPIV_ALGO_RSA2048 => { + let key_len = if algorithm == YKPIV_ALGO_RSA1024 { + 128 + } else { + 256 + }; + + if in_len != key_len { + return Err(Error::SizeError); + } else { + _currentBlock = 16; + } + } + YKPIV_ALGO_ECCP256 | YKPIV_ALGO_ECCP384 => { + let key_len = if algorithm == YKPIV_ALGO_ECCP256 { + 32 + } else { + 48 + }; + + if (!decipher && (in_len > key_len)) || (decipher && (in_len != (key_len * 2) + 1)) + { + return Err(Error::SizeError); + } + } + _ => return Err(Error::AlgorithmError), + } + + if in_len < 0x80 { + bytes = 1; + } else if in_len < 0xff { + bytes = 2; + } else { + bytes = 3; + } + + *dataptr = 0x7c; + dataptr = dataptr.add(_ykpiv_set_length(dataptr, in_len + bytes + 3)); + *dataptr = 0x82; + *dataptr.add(1) = 0x00; + *dataptr.add(2) = + if (algorithm == YKPIV_ALGO_ECCP256 || algorithm == YKPIV_ALGO_ECCP384) && decipher { + 0x85 + } else { + 0x81 + }; + dataptr = dataptr.add(3 + _ykpiv_set_length(dataptr, in_len)); + memcpy(dataptr as *mut c_void, sign_in as *const c_void, in_len); + dataptr = dataptr.add(in_len); + + if let Err(e) = self.ykpiv_transfer_data( + templ.as_ptr(), + indata.as_mut_ptr(), + dataptr as isize - indata.as_mut_ptr() as isize, + data.as_mut_ptr(), + &mut recv_len, + &mut sw, + ) { + error!("sign command failed to communicate"); + return Err(e); + } + + if sw != SW_SUCCESS { + error!("Failed sign command with code {:x}", sw); + + if sw == SW_ERR_SECURITY_STATUS { + return Err(Error::AuthenticationError); + } else { + return Err(Error::GenericError); + } + } + + // skip the first 7c tag + if data[0] != 0x7c { + error!("failed parsing signature reply (0x7c byte)"); + return Err(Error::ParseError); + } + + dataptr = data.as_mut_ptr().add(1); + dataptr = dataptr.add(_ykpiv_get_length(dataptr, &mut len)); + + // skip the 82 tag + if *dataptr != 0x82 { + error!("failed parsing signature reply (0x82 byte)"); + return Err(Error::ParseError); + } + + dataptr = dataptr.add(1); + dataptr = dataptr.add(_ykpiv_get_length(dataptr, &mut len)); + + if len > *out_len { + error!("wrong size on output buffer"); + return Err(Error::SizeError); + } + + *out_len = len; + memcpy(out as (*mut c_void), dataptr as (*const c_void), len); + Ok(()) + } + + /// Sign data using a PIV key + pub unsafe fn ykpiv_sign_data( + &mut self, + raw_in: *const u8, + in_len: usize, + sign_out: *mut u8, + out_len: *mut usize, + algorithm: u8, + key: u8, + ) -> Result<(), Error> { + self._ykpiv_begin_transaction()?; + + // don't attempt to reselect in crypt operations to avoid problems with PIN_ALWAYS + + let res = + self._general_authenticate(raw_in, in_len, sign_out, out_len, algorithm, key, false); + + let _ = self._ykpiv_end_transaction(); + res + } + + /// Decrypt data using a PIV key + pub unsafe fn ykpiv_decrypt_data( + &mut self, + input: *const u8, + input_len: usize, + out: *mut u8, + out_len: *mut usize, + algorithm: u8, + key: u8, + ) -> Result<(), Error> { + self._ykpiv_begin_transaction()?; + + // don't attempt to reselect in crypt operations to avoid problems with PIN_ALWAYS + + let res = self._general_authenticate(input, input_len, out, out_len, algorithm, key, true); + let _ = self._ykpiv_end_transaction(); + res + } + + /// Get the version of the PIV application installed on the YubiKey + pub(crate) unsafe fn _ykpiv_get_version(&mut self) -> Result { + let mut data = [0u8; 261]; + let mut recv_len = data.len() as u32; + let mut sw: i32 = 0; + + // get version from self if already from device + if self.ver.major != 0 || self.ver.minor != 0 || self.ver.patch != 0 { + return Ok(self.ver); + } + + // get version from device + let mut apdu = APDU::default(); + apdu.ins = YKPIV_INS_GET_VERSION; + + self._send_data(&mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw)?; + + if sw != SW_SUCCESS { + return Err(Error::GenericError); + } + + if recv_len < 3 { + return Err(Error::SizeError); + } + + self.ver.major = data[0]; + self.ver.minor = data[1]; + self.ver.patch = data[2]; + + Ok(self.ver) + } + + /// Get the YubiKey's PIV application version as a string + pub unsafe fn ykpiv_get_version(&mut self) -> Result { + let mut res = Err(Error::GenericError); + + self._ykpiv_begin_transaction()?; + + if self._ykpiv_ensure_application_selected().is_ok() { + res = self._ykpiv_get_version(); + } + + let _ = self._ykpiv_end_transaction(); + res.map(|ver| format!("{}.{}.{}", ver.major, ver.minor, ver.patch)) + } + + /// Get YubiKey device serial number + /// + /// NOTE: caller must make sure that this is wrapped in a transaction for synchronized operation + pub(crate) unsafe fn _ykpiv_get_serial(&mut self, f_force: bool) -> Result { + let yk_applet: *const u8 = ptr::null(); + let mut data = [0u8; 255]; + let mut recv_len = data.len() as u32; + let mut sw: i32 = 0; + let p_temp: *mut u8; + + if !f_force && self.serial != 0 { + return Ok(self.serial); + } + + if self.ver.major < 5 { + // get serial from neo/yk4 devices using the otp applet + let mut temp = [0u8; 255]; + recv_len = temp.len() as u32; + + let mut apdu = APDU::default(); + apdu.ins = YKPIV_INS_SELECT_APPLICATION; + apdu.p1 = 0x04; + apdu.lc = mem::size_of_val(&yk_applet) as u8; + + memcpy( + apdu.data.as_mut_ptr() as *mut c_void, + yk_applet as *const c_void, + mem::size_of_val(&yk_applet), + ); + + if let Err(e) = self._send_data(&mut apdu, temp.as_mut_ptr(), &mut recv_len, &mut sw) { + error!("failed communicating with card: '{}'", e); + return Err(e); + } + + if sw != SW_SUCCESS { + error!("failed selecting yk application: {:04x}", sw); + return Err(Error::GenericError); + } + + recv_len = temp.len() as u32; + apdu = APDU::default(); + apdu.ins = 0x01; + apdu.p1 = 0x10; + apdu.lc = 0x00; + + if let Err(e) = self._send_data(&mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw) { + error!("failed communicating with card: '{}'", e); + return Err(e); + } + + if sw != SW_SUCCESS { + error!("failed retrieving serial number: {:04x}", sw); + return Err(Error::GenericError); + } + + recv_len = temp.len() as u32; + apdu = APDU::default(); + apdu.ins = YKPIV_INS_SELECT_APPLICATION; + apdu.p1 = 0x04; + apdu.lc = mem::size_of_val(&AID) as u8; + + memcpy( + apdu.data.as_mut_ptr() as *mut c_void, + AID.as_ptr() as *const c_void, + mem::size_of_val(&AID), + ); + + if let Err(e) = self._send_data(&mut apdu, temp.as_mut_ptr(), &mut recv_len, &mut sw) { + error!("failed communicating with card: '{}'", e); + return Err(e); + } + + if sw != SW_SUCCESS { + error!("failed selecting application: {:04x}", sw); + return Err(Error::GenericError); + } + } else { + // get serial from yk5 and later devices using the f8 command + let mut apdu = APDU::default(); + apdu.ins = YKPIV_INS_GET_SERIAL; + + if let Err(e) = self._send_data(&mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw) { + error!("failed communicating with card: '{}'", e); + return Err(e); + } + + if sw != SW_SUCCESS { + error!("failed retrieving serial number: {:04x}", sw); + return Err(Error::GenericError); + } + } + + // check that we received enough data for the serial number + if recv_len < 4 { + return Err(Error::SizeError); + } + + // TODO(tarcieri): replace pointers and casts with proper references! + #[allow(trivial_casts)] + { + p_temp = &mut self.serial as (*mut u32) as (*mut u8); + } + + *p_temp = data[3]; + *p_temp.add(1) = data[2]; + *p_temp.add(2) = data[1]; + *p_temp.add(3) = data[0]; + + Ok(self.serial) + } + + /// Get YubiKey device serial number + pub unsafe fn ykpiv_get_serial(&mut self) -> Result { + let mut res = Err(Error::GenericError); + + self._ykpiv_begin_transaction()?; + + if self._ykpiv_ensure_application_selected().is_ok() { + res = self._ykpiv_get_serial(false); + } + + let _ = self._ykpiv_end_transaction(); + res + } + + /// Cache PIN in memory + // TODO(tarcieri): better security around the cached PIN + pub(crate) unsafe fn _cache_pin( + &mut self, + pin: *const c_char, + len: usize, + ) -> Result<(), Error> { + if !pin.is_null() && (self.pin as *const c_char == pin) { + return Ok(()); + } + + if !self.pin.is_null() { + // Zeroize the old cached PIN + let old_len = strlen(self.pin as *const c_char); + let pin_slice = slice::from_raw_parts_mut(self.pin, old_len); + pin_slice.zeroize(); + + free(self.pin as (*mut c_void)); + self.pin = ptr::null_mut(); + } + + if !pin.is_null() && len > 0 { + self.pin = malloc(len + 1) as (*mut u8); + + if self.pin.is_null() { + return Err(Error::MemoryError); + } + + memcpy(self.pin as (*mut c_void), pin as (*const c_void), len); + *self.pin.add(len) = 0u8; + } + + Ok(()) + } + + /// Verify device PIN + /// + /// Returns the number of tries remaining both on success and on a wrong PIN. + pub unsafe fn ykpiv_verify(&mut self, pin: *const c_char) -> Result { + self.ykpiv_verify_select(pin, if !pin.is_null() { strlen(pin) } else { 0 }, false) + } + + /// Verify device PIN + /// + /// Returns the number of tries remaining both on success and on a wrong PIN. + pub(crate) unsafe fn _verify( + &mut self, + pin: *const c_char, + pin_len: usize, + ) -> Result { + let mut data = [0u8; 261]; + let mut recv_len = data.len() as u32; + let mut sw: i32 = 0; + + if pin_len > CB_PIN_MAX { + return Err(Error::SizeError); + } + + let mut apdu = APDU::default(); + apdu.ins = YKPIV_INS_VERIFY; + apdu.p1 = 0x00; + apdu.p2 = 0x80; + apdu.lc = if pin.is_null() { 0 } else { 0x08 }; + + if !pin.is_null() { + memcpy( + apdu.data.as_mut_ptr() as *mut c_void, + pin as *const c_void, + pin_len, + ); + + if pin_len < CB_PIN_MAX { + memset( + apdu.data.as_mut_ptr().add(pin_len) as *mut c_void, + 0xff, + CB_PIN_MAX - pin_len, + ); + } + } + + let res = self._send_data(&mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw); + + apdu.zeroize(); + + if let Err(e) = res { + return Err(e); + } + + if sw == SW_SUCCESS { + if !pin.is_null() && (pin_len != 0) { + // Intentionally ignore errors. If the PIN fails to save, it will only + // be a problem if a reconnect is attempted. Failure deferred until then. + let _ = self._cache_pin(pin, pin_len); + } + + Ok(sw & 0xf) + } else if sw >> 8 == 0x63 { + Err(Error::WrongPin { tries: sw & 0xf }) + } else if sw == SW_ERR_AUTH_BLOCKED { + Err(Error::WrongPin { tries: 0 }) + } else { + Err(Error::GenericError) + } + } + + /// Verify and select application + /// + /// Returns the number of tries remaining both on success and on a wrong PIN. + pub unsafe fn ykpiv_verify_select( + &mut self, + pin: *const c_char, + pin_len: usize, + force_select: bool, + ) -> Result { + let mut res = Ok(-1); + + self._ykpiv_begin_transaction()?; + + if force_select { + if let Err(e) = self._ykpiv_ensure_application_selected() { + res = Err(e); + } + } + + if res.is_ok() { + res = self._verify(pin, pin_len); + } + + let _ = self._ykpiv_end_transaction(); + res + } + + /// Get the number of PIN retries + pub unsafe fn ykpiv_get_pin_retries(&mut self) -> Result { + // Force a re-select to unverify, because once verified the spec dictates that + // subsequent verify calls will return a "verification not needed" instead of + // the number of tries left... + self._ykpiv_select_application()?; + + let ykrc = self.ykpiv_verify(ptr::null()); + + // WRONG_PIN is expected on successful query. + match ykrc { + Ok(tries) | Err(Error::WrongPin { tries }) => Ok(tries), + Err(e) => Err(e), + } + } + + /// Set the number of PIN retries + pub unsafe fn ykpiv_set_pin_retries( + &mut self, + pin_tries: i32, + puk_tries: i32, + ) -> Result<(), Error> { + let mut res = Ok(()); + let mut templ = [0, YKPIV_INS_SET_PIN_RETRIES, 0, 0]; + let mut data = [0u8; 255]; + let mut recv_len: usize = data.len(); + let mut sw: i32 = 0i32; + + // Special case: if either retry count is 0, it's a successful no-op + if pin_tries == 0 || puk_tries == 0 { + return Ok(()); + } + + if pin_tries > 0xff || puk_tries > 0xff || pin_tries < 1 || puk_tries < 1 { + return Err(Error::RangeError); + } + + templ[2] = pin_tries as (u8); + templ[3] = puk_tries as (u8); + + self._ykpiv_begin_transaction()?; + + if self._ykpiv_ensure_application_selected().is_ok() { + res = self.ykpiv_transfer_data( + templ.as_ptr(), + ptr::null(), + 0, + data.as_mut_ptr(), + &mut recv_len, + &mut sw, + ); + + if res.is_ok() { + res = match sw { + SW_SUCCESS => Ok(()), + SW_ERR_AUTH_BLOCKED => Err(Error::AuthenticationError), + SW_ERR_SECURITY_STATUS => Err(Error::AuthenticationError), + _ => Err(Error::GenericError), + }; + } + } + + let _ = self._ykpiv_end_transaction(); + res + } + + /// Change the PIN + pub(crate) unsafe fn _ykpiv_change_pin( + &mut self, + action: i32, + current_pin: *const c_char, + current_pin_len: usize, + new_pin: *const c_char, + new_pin_len: usize, + ) -> Result<(), Error> { + let mut sw: i32 = 0; + let mut templ = [0, YKPIV_INS_CHANGE_REFERENCE, 0, 0x80]; + let mut indata = [0u8; 16]; + let mut data = [0u8; 255]; + let mut recv_len: usize = data.len(); + + if current_pin_len > 8 || new_pin_len > 8 { + return Err(Error::SizeError); + } + + if action == CHREF_ACT_UNBLOCK_PIN { + templ[1] = YKPIV_INS_RESET_RETRY; + } else if action == CHREF_ACT_CHANGE_PUK { + templ[3] = 0x81; + } + + memcpy( + indata.as_mut_ptr() as (*mut c_void), + current_pin as (*const c_void), + current_pin_len, + ); + + if current_pin_len < 8 { + memset( + indata.as_mut_ptr().add(current_pin_len) as *mut c_void, + 0xff, + 8 - current_pin_len, + ); + } + + memcpy( + indata.as_mut_ptr().offset(8) as *mut c_void, + new_pin as *const c_void, + new_pin_len, + ); + + if new_pin_len < 8 { + memset( + indata.as_mut_ptr().offset(8).add(new_pin_len) as *mut c_void, + 0xff, + 8 - new_pin_len, + ); + } + + let res = self.ykpiv_transfer_data( + templ.as_ptr(), + indata.as_mut_ptr(), + indata.len() as isize, + data.as_mut_ptr(), + &mut recv_len, + &mut sw, + ); + + indata.zeroize(); + + if res.is_err() { + return res; + } + + if sw != SW_SUCCESS { + if sw >> 8 == 0x63 { + return Err(Error::WrongPin { tries: sw & 0xf }); + } + + if sw == SW_ERR_AUTH_BLOCKED { + return Err(Error::PinLocked); + } + + error!("failed changing pin, token response code: {:x}.", sw); + return Err(Error::GenericError); + } + + Ok(()) + } + + /// Change the Personal Identification Number (PIN). + /// + /// The default PIN code is 123456 + pub unsafe fn ykpiv_change_pin( + &mut self, + current_pin: *const c_char, + current_pin_len: usize, + new_pin: *const c_char, + new_pin_len: usize, + ) -> Result<(), Error> { + let mut res = Err(Error::GenericError); + + self._ykpiv_begin_transaction()?; + + if self._ykpiv_ensure_application_selected().is_ok() { + res = self._ykpiv_change_pin(0, current_pin, current_pin_len, new_pin, new_pin_len); + + if res.is_ok() && !new_pin.is_null() { + // Intentionally ignore errors. If the PIN fails to save, it will only + // be a problem if a reconnect is attempted. Failure deferred until then. + let _ = self._cache_pin(new_pin, new_pin_len); + } + } + + let _ = self._ykpiv_end_transaction(); + res + } + + /// Change the PIN Unblocking Key (PUK). PUKs are codes for resetting + /// lost/forgotten PINs, or devices that have become blocked because of too + /// many failed attempts. + /// + /// The PUK is part of the PIV standard that the YubiKey follows. + /// + /// The default PUK code is 12345678. + pub unsafe fn ykpiv_change_puk( + &mut self, + current_puk: *const c_char, + current_puk_len: usize, + new_puk: *const c_char, + new_puk_len: usize, + ) -> Result<(), Error> { + let mut res = Err(Error::GenericError); + + self._ykpiv_begin_transaction()?; + + if self._ykpiv_ensure_application_selected().is_ok() { + res = self._ykpiv_change_pin(2, current_puk, current_puk_len, new_puk, new_puk_len); + } + + let _ = self._ykpiv_end_transaction(); + res + } + + /// Unblock a Personal Identification Number (PIN) using a previously + /// configured PIN Unblocking Key (PUK). + pub unsafe fn ykpiv_unblock_pin( + &mut self, + puk: *const c_char, + puk_len: usize, + new_pin: *const c_char, + new_pin_len: usize, + ) -> Result<(), Error> { + let mut res = Err(Error::GenericError); + + self._ykpiv_begin_transaction()?; + + if self._ykpiv_ensure_application_selected().is_ok() { + res = self._ykpiv_change_pin(1, puk, puk_len, new_pin, new_pin_len); + } + + let _ = self._ykpiv_end_transaction(); + res + } + + /// Fetch an object from the YubiKey + pub unsafe fn ykpiv_fetch_object( + &mut self, + object_id: i32, + data: *mut u8, + len: *mut usize, + ) -> Result<(), Error> { + let mut res = Ok(()); + + self._ykpiv_begin_transaction()?; + + if self._ykpiv_ensure_application_selected().is_ok() { + res = self._ykpiv_fetch_object(object_id, data, len); + } + + let _ = self._ykpiv_end_transaction(); + res + } + + /// Fetch an object + pub(crate) unsafe fn _ykpiv_fetch_object( + &mut self, + object_id: i32, + data: *mut u8, + len: *mut usize, + ) -> Result<(), Error> { + let mut sw: i32 = 0; + let mut indata = [0u8; 5]; + let mut inptr: *mut u8 = indata.as_mut_ptr(); + let templ = [0, YKPIV_INS_GET_DATA, 0x3f, 0xff]; + + inptr = set_object(object_id, inptr); + + if inptr.is_null() { + return Err(Error::InvalidObject); + } + + self.ykpiv_transfer_data( + templ.as_ptr(), + indata.as_mut_ptr(), + inptr as isize - indata.as_mut_ptr() as isize, + data, + len, + &mut sw, + )?; + + if sw != SW_SUCCESS { + return Err(Error::GenericError); + } + + let mut outlen: usize = 0; + + if *len < 2 || !_ykpiv_has_valid_length(data.offset(1), (*len).wrapping_sub(1)) { + return Err(Error::SizeError); + } + + let offs = _ykpiv_get_length(data.offset(1), &mut outlen); + + if offs == 0 { + return Err(Error::SizeError); + } + + if outlen.wrapping_add(offs).wrapping_add(1) != *len { + error!( + "invalid length indicated in object: total len is {} but indicated length is {}", + *len, outlen + ); + + return Err(Error::SizeError); + } + + memmove( + data as *mut c_void, + data.add(1).add(offs) as *const c_void, + outlen, + ); + *len = outlen; + + Ok(()) + } + + /// Save an object + pub unsafe fn ykpiv_save_object( + &mut self, + object_id: i32, + indata: *mut u8, + len: usize, + ) -> Result<(), Error> { + let mut res = Ok(()); + + self._ykpiv_begin_transaction()?; + + if self._ykpiv_ensure_application_selected().is_ok() { + res = self._ykpiv_save_object(object_id, indata, len); + } + + let _ = self._ykpiv_end_transaction(); + res + } + + /// Save an object + pub unsafe fn _ykpiv_save_object( + &mut self, + object_id: i32, + indata: *mut u8, + len: usize, + ) -> Result<(), Error> { + let mut data = [0u8; YKPIV_OBJ_MAX_SIZE]; + let mut dataptr: *mut u8 = data.as_mut_ptr(); + let templ = [0, YKPIV_INS_PUT_DATA, 0x3f, 0xff]; + let mut sw: i32 = 0; + let mut outlen: usize = 0usize; + + if len > CB_OBJ_MAX { + return Err(Error::SizeError); + } + + dataptr = set_object(object_id, dataptr); + + if dataptr.is_null() { + return Err(Error::InvalidObject); + } + *{ + let _old = dataptr; + dataptr = dataptr.offset(1); + _old + } = 0x53; + + dataptr = dataptr.add(_ykpiv_set_length(dataptr, len)); + memcpy(dataptr as (*mut c_void), indata as (*const c_void), len); + dataptr = dataptr.add(len); + + self._ykpiv_transfer_data( + templ.as_ptr(), + data.as_mut_ptr(), + dataptr as isize - data.as_mut_ptr() as isize, + ptr::null_mut(), + &mut outlen, + &mut sw, + )?; + + match sw { + SW_SUCCESS => Ok(()), + SW_ERR_SECURITY_STATUS => Err(Error::AuthenticationError), + _ => Err(Error::GenericError), + } + } + + /// Import a private encryption or signing key into the YubiKey + pub unsafe fn ykpiv_import_private_key( + &mut self, + key: u8, + algorithm: u8, + p: *const u8, + p_len: usize, + q: *const u8, + q_len: usize, + dp: *const u8, + dp_len: usize, + dq: *const u8, + dq_len: usize, + qinv: *const u8, + qinv_len: usize, + ec_data: *const u8, + ec_data_len: u8, + pin_policy: u8, + touch_policy: u8, + ) -> Result<(), Error> { + let mut key_data = [0u8; 1024]; + let mut in_ptr: *mut u8 = key_data.as_mut_ptr(); + let templ = [0, YKPIV_INS_IMPORT_KEY, algorithm, key]; + let mut data = [0u8; 256]; + let mut recv_len = data.len(); + let mut elem_len: u32 = 0; + let mut sw: i32 = 0; + let mut params: [*const u8; 5] = [ptr::null(); 5]; + let mut lens = [0usize; 5]; + let n_params: u8; + let param_tag: i32; + + if key == YKPIV_KEY_CARDMGM + || key < YKPIV_KEY_RETIRED1 + || key > YKPIV_KEY_RETIRED20 && (key < YKPIV_KEY_AUTHENTICATION) + || key > YKPIV_KEY_CARDAUTH && (key != YKPIV_KEY_ATTESTATION) + { + return Err(Error::KeyError); + } + + if pin_policy != YKPIV_PINPOLICY_DEFAULT + && (pin_policy != YKPIV_PINPOLICY_NEVER) + && (pin_policy != YKPIV_PINPOLICY_ONCE) + && (pin_policy != YKPIV_PINPOLICY_ALWAYS) + { + return Err(Error::GenericError); + } + + if touch_policy != YKPIV_TOUCHPOLICY_DEFAULT + && (touch_policy != YKPIV_TOUCHPOLICY_NEVER) + && (touch_policy != YKPIV_TOUCHPOLICY_ALWAYS) + && (touch_policy != YKPIV_TOUCHPOLICY_CACHED) + { + return Err(Error::GenericError); + } + + match algorithm { + YKPIV_ALGO_RSA1024 | YKPIV_ALGO_RSA2048 => { + if p_len + q_len + dp_len + dq_len + qinv_len >= 1024 { + return Err(Error::SizeError); + } else { + if algorithm == YKPIV_ALGO_RSA1024 { + elem_len = 64; + } + + if algorithm == YKPIV_ALGO_RSA2048 { + elem_len = 128; + } + + if p.is_null() || q.is_null() || dp.is_null() || dq.is_null() || qinv.is_null() + { + return Err(Error::GenericError); + } + + params[0] = p; + lens[0] = p_len; + params[1] = q; + lens[1] = q_len; + params[2] = dp; + lens[2] = dp_len; + params[3] = dq; + lens[3] = dq_len; + params[4] = qinv; + lens[4] = qinv_len; + param_tag = 0x1; + n_params = 5u8; + } + } + YKPIV_ALGO_ECCP256 | YKPIV_ALGO_ECCP384 => { + if ec_data_len as (usize) >= key_data.len() { + return Err(Error::SizeError); + } + + if algorithm == YKPIV_ALGO_ECCP256 { + elem_len = 32; + } else if algorithm == YKPIV_ALGO_ECCP384 { + elem_len = 48; + } + + if ec_data.is_null() { + return Err(Error::GenericError); + } + + params[0] = ec_data; + lens[0] = ec_data_len as usize; + param_tag = 0x6; + n_params = 1; + } + _ => return Err(Error::AlgorithmError), + } + + for i in 0..n_params { + *in_ptr = (param_tag + i as i32) as u8; + in_ptr = in_ptr.offset(1); + + in_ptr = in_ptr.add(_ykpiv_set_length(in_ptr, elem_len as usize)); + let padding = (elem_len as (usize)).wrapping_sub(lens[i as usize]); + let remaining = (key_data.as_mut_ptr() as usize) + 1024 - in_ptr as usize; + + if padding > remaining { + return Err(Error::AlgorithmError); + } + + memset(in_ptr as *mut c_void, 0, padding); + in_ptr = in_ptr.add(padding); + memcpy( + in_ptr as *mut c_void, + params[i as usize] as *const c_void, + lens[i as usize], + ); + in_ptr = in_ptr.add(lens[i as usize]); + } + + if pin_policy != YKPIV_PINPOLICY_DEFAULT { + *in_ptr = YKPIV_PINPOLICY_TAG; + *in_ptr.add(1) = 0x01; + *in_ptr.add(2) = pin_policy; + in_ptr = in_ptr.add(3); + } + + if touch_policy != YKPIV_TOUCHPOLICY_DEFAULT { + *in_ptr = YKPIV_TOUCHPOLICY_TAG; + *in_ptr.add(1) = 0x01; + *in_ptr.add(2) = touch_policy; + in_ptr = in_ptr.add(3); + } + + self._ykpiv_begin_transaction()?; + + let mut res = Ok(()); + if self._ykpiv_ensure_application_selected().is_ok() { + res = self.ykpiv_transfer_data( + templ.as_ptr(), + key_data.as_mut_ptr(), + in_ptr as isize - key_data.as_mut_ptr() as isize, + data.as_mut_ptr(), + &mut recv_len, + &mut sw, + ); + + if res.is_ok() && sw != SW_SUCCESS { + res = Err(Error::GenericError); + if sw == SW_ERR_SECURITY_STATUS { + res = Err(Error::AuthenticationError); + } + } + } + + key_data.zeroize(); + let _ = self._ykpiv_end_transaction(); + res + } + + /// Generate an attestation certificate for a stored key + pub unsafe fn ykpiv_attest( + &mut self, + key: u8, + data: *mut u8, + data_len: *mut usize, + ) -> Result<(), Error> { + let mut res = Err(Error::GenericError); + let templ = [0, YKPIV_INS_ATTEST, key, 0]; + let mut sw: i32 = 0; + let mut ul_data_len: usize; + + if data.is_null() || data_len.is_null() { + return Err(Error::ArgumentError); + } + + ul_data_len = *data_len; + + self._ykpiv_begin_transaction()?; + + if self._ykpiv_ensure_application_selected().is_ok() { + res = self.ykpiv_transfer_data( + templ.as_ptr(), + ptr::null(), + 0, + data, + &mut ul_data_len, + &mut sw, + ); + + if res.is_ok() { + if sw != SW_SUCCESS { + res = Err(Error::GenericError); + if sw == SW_ERR_NOT_SUPPORTED { + res = Err(Error::NotSupported); + } + } else if *data as i32 != 0x30 { + res = Err(Error::GenericError); + } else { + *data_len = ul_data_len; + } + } + } + + let _ = self._ykpiv_end_transaction(); + res + } + + /// Get an auth challenge + pub unsafe fn ykpiv_auth_getchallenge(&mut self) -> Result<[u8; 8], Error> { + let mut data = [0u8; 261]; + let mut recv_len = data.len() as u32; + let mut sw: i32 = 0; + // TODO(str4d): What should the default value be if the application is not selected? + let mut res = Ok([0; 8]); + + self._ykpiv_begin_transaction()?; + + if self._ykpiv_ensure_application_selected().is_ok() { + let mut apdu = APDU::default(); + apdu.ins = YKPIV_INS_AUTHENTICATE; + apdu.p1 = YKPIV_ALGO_3DES; // triple des + apdu.p2 = YKPIV_KEY_CARDMGM; // management key + apdu.lc = 0x04; + apdu.data[0] = 0x7c; + apdu.data[1] = 0x02; + apdu.data[2] = 0x81; //0x80; + + if let Err(e) = self._send_data(&mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw) { + res = Err(e) + } else if sw != SW_SUCCESS { + res = Err(Error::AuthenticationError); + } else { + let mut challenge = [0; 8]; + challenge.copy_from_slice(&data[4..12]); + res = Ok(challenge); + } + } + + let _ = self._ykpiv_end_transaction(); + res + } + + /// Verify an auth response + pub unsafe fn ykpiv_auth_verifyresponse(&mut self, response: [u8; 8]) -> Result<(), Error> { + let mut data = [0u8; 261]; + let mut recv_len = data.len() as u32; + let mut sw: i32 = 0; + + self._ykpiv_begin_transaction()?; + + // send the response to the card and a challenge of our own. + let mut apdu = APDU::default(); + apdu.ins = YKPIV_INS_AUTHENTICATE; + apdu.p1 = YKPIV_ALGO_3DES; // triple des + apdu.p2 = YKPIV_KEY_CARDMGM; // management key + apdu.data[0] = 0x7c; + apdu.data[1] = 0x0a; // 2 + 8 + apdu.data[2] = 0x82; + apdu.data[3] = 8; + apdu.data[4..12].copy_from_slice(&response); + apdu.lc = 12; + + let mut res = self._send_data(&mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw); + + if res.is_ok() && sw != SW_SUCCESS { + res = Err(Error::AuthenticationError); + } + + apdu.zeroize(); + let _ = self._ykpiv_end_transaction(); + res + } + + /// Deauthenticate + pub unsafe fn ykpiv_auth_deauthenticate(&mut self) -> Result<(), Error> { + let mut data = [0u8; 255]; + let mut recv_len = data.len() as u32; + let mut sw: i32 = 0; + + self._ykpiv_begin_transaction()?; + + let mut apdu = APDU::default(); + apdu.ins = YKPIV_INS_SELECT_APPLICATION; + apdu.p1 = 0x04; + apdu.lc = mem::size_of::<*const u8>() as u8; + + memcpy( + apdu.data.as_mut_ptr() as *mut c_void, + MGMT_AID.as_ptr() as *const c_void, + MGMT_AID.len(), + ); + + let mut res = self._send_data(&mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw); + + if let Err(e) = &res { + error!("failed communicating with card: \'{}\'", e); + } + + if sw != SW_SUCCESS { + error!("Failed selecting mgmt application: {:04x}", sw); + res = Err(Error::GenericError); + } + + let _ = self._ykpiv_end_transaction(); + res + } +} + /// Set length pub(crate) unsafe fn _ykpiv_set_length(buffer: *mut u8, length: usize) -> usize { if length < 0x80 { @@ -175,1594 +2074,6 @@ pub(crate) unsafe fn _ykpiv_has_valid_length(buffer: *const u8, len: usize) -> b || ((*buffer as i32 & 0x7f == 2) && (len > 2)) } -/// Initialize YubiKey client instance -pub fn ykpiv_init() -> YubiKey { - YubiKey { - context: -1, - card: 0, - pin: ptr::null_mut(), - is_neo: false, - ver: Version { - major: 0, - minor: 0, - patch: 0, - }, - serial: 0, - } -} - -/// Cleanup YubiKey session -pub(crate) unsafe fn _ykpiv_done(state: &mut YubiKey, disconnect: bool) -> Result<(), Error> { - if disconnect { - ykpiv_disconnect(state); - } - - let _ = _cache_pin(state, ptr::null(), 0); - Ok(()) -} - -/// Cleanup YubiKey session with external card upon completion -// TODO(tarcieri): make this a `Drop` handler -pub unsafe fn ykpiv_done_with_external_card(state: &mut YubiKey) -> Result<(), Error> { - _ykpiv_done(state, false) -} - -/// Cleanup YubiKey session upon completion -pub unsafe fn ykpiv_done(state: &mut YubiKey) -> Result<(), Error> { - _ykpiv_done(state, true) -} - -/// Disconnect a YubiKey session -pub unsafe fn ykpiv_disconnect(state: &mut YubiKey) { - if state.card != 0 { - SCardDisconnect(state.card, 0x1); - state.card = 0i32; - } - - if SCardIsValidContext(state.context) == 0x0 { - SCardReleaseContext(state.context); - state.context = -1i32; - } -} - -/// Select application -pub(crate) unsafe fn _ykpiv_select_application(state: &mut YubiKey) -> Result<(), Error> { - let mut data = [0u8; 255]; - let mut recv_len = data.len() as u32; - let mut sw = 0i32; - - let mut apdu = APDU::default(); - apdu.ins = YKPIV_INS_SELECT_APPLICATION; - apdu.p1 = 0x04; - apdu.lc = AID.len() as u8; - - memcpy( - apdu.data.as_mut_ptr() as *mut c_void, - AID.as_ptr() as *const c_void, - AID.len(), - ); - - if let Err(e) = _send_data(state, &mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw) { - error!("failed communicating with card: \'{}\'", e); - return Err(e); - } - - if sw != SW_SUCCESS { - error!("failed selecting application: {:04x}", sw); - return Err(Error::GenericError); - } - - // now that the PIV application is selected, retrieve the version - // and serial number. Previously the NEO/YK4 required switching - // to the yk applet to retrieve the serial, YK5 implements this - // as a PIV applet command. Unfortunately, this change requires - // that we retrieve the version number first, so that get_serial - // can determine how to get the serial number, which for the NEO/Yk4 - // will result in another selection of the PIV applet. - - if let Err(e) = _ykpiv_get_version(state) { - warn!("failed to retrieve version: \'{}\'", e); - } - - if let Err(e) = _ykpiv_get_serial(state, false) { - warn!("failed to retrieve serial number: \'{}\'", e); - } - - Ok(()) -} - -/// Ensure an application is selected (presently noop) -pub(crate) unsafe fn _ykpiv_ensure_application_selected(_state: &mut YubiKey) -> Result<(), Error> { - // TODO(tarcieri): ENABLE_APPLICATION_RESELECTION support? - // - // Original C code below: - // - // #if ENABLE_APPLICATION_RESELECTION - // if (NULL == state) { - // return YKPIV_GENERIC_ERROR; - // } - // - // res = ykpiv_verify(state, NULL, 0); - // - // if ((YKPIV_OK != res) && (YKPIV_WRONG_PIN != res)) { - // res = _ykpiv_select_application(state); - // } - // else { - // res = YKPIV_OK; - // } - // - // return res; - // #else - // (void)state; - // return res; - // #endif - - Ok(()) -} - -/// Connect to the YubiKey -pub(crate) unsafe fn _ykpiv_connect( - state: &mut YubiKey, - context: usize, - card: usize, -) -> Result<(), Error> { - // if the context has changed, and the new context is not valid, return an error - if context != state.context as (usize) && (0x0i32 != SCardIsValidContext(context as (i32))) { - return Err(Error::PcscError); - } - - // if card handle has changed, determine if handle is valid (less efficient, but complete) - if card != state.card as usize { - let mut reader = [0u8; YKPIV_OBJ_MAX_SIZE]; - let mut reader_len = reader.len() as u32; - let mut atr = [0u8; 33]; - let mut atr_len = atr.len() as u32; - - // Cannot set the reader len to NULL. Confirmed in OSX 10.10, - // so we have to retrieve it even though we don't need it. - if SCardStatus( - card as i32, - reader.as_mut_ptr(), - &mut reader_len, - ptr::null_mut(), - ptr::null_mut(), - atr.as_mut_ptr(), - &mut atr_len, - ) != 0 - { - return Err(Error::PcscError); - } - - state.is_neo = (atr_len as usize == YKPIV_ATR_NEO_R3.len() - 1) - && (memcmp( - YKPIV_ATR_NEO_R3.as_ptr() as *const c_void, - atr.as_mut_ptr() as *const c_void, - atr_len as usize, - ) == 0); - } - - state.context = context as i32; - state.card = card as i32; - - // Do not select the applet here, as we need to accommodate commands that are - // sensitive to re-select (custom apdu/auth). All commands that can handle explicit - // selection already check the applet state and select accordingly anyway. - // ykpiv_verify_select is supplied for those who want to select explicitly. - // - // The applet _is_ selected by ykpiv_connect(), but is not selected when bypassing - // it with ykpiv_connect_with_external_card(). - - Ok(()) -} - -/// Connect to an external card -pub unsafe fn ykpiv_connect_with_external_card( - state: &mut YubiKey, - context: usize, - card: usize, -) -> Result<(), Error> { - _ykpiv_connect(state, context, card) -} - -/// Connect to a YubiKey -pub unsafe fn ykpiv_connect(state: &mut YubiKey, wanted: *const c_char) -> Result<(), Error> { - let mut active_protocol: u32 = 0; - let mut reader_buf: [c_char; 2048] = [0; 2048]; - let mut num_readers = reader_buf.len(); - let mut reader_ptr: *mut c_char; - let mut card: i32 = -1i32; - - ykpiv_list_readers(state, reader_buf.as_mut_ptr(), &mut num_readers)?; - - reader_ptr = reader_buf.as_mut_ptr(); - - while *reader_ptr != 0 { - if !wanted.is_null() { - let mut ptr = reader_ptr; - let mut found = false; - - while *ptr != 0 { - if strlen(ptr) < strlen(wanted) { - break; - } - - if strncasecmp(ptr, wanted, strlen(wanted)) == 0 { - found = true; - break; - } - - ptr = ptr.add(1); - } - - if !found { - warn!( - "skipping reader \'{}\' since it doesn\'t match \'{}\'", - CStr::from_ptr(reader_ptr).to_string_lossy(), - CStr::from_ptr(wanted).to_string_lossy() - ); - - continue; - } - } - - info!( - "trying to connect to reader \'{}\'", - CStr::from_ptr(reader_ptr).to_string_lossy() - ); - - let rc = SCardConnect( - state.context, - reader_ptr, - 0x2u32, - 0x2u32, - &mut card, - &mut active_protocol, - ); - - if rc != 0x0 { - error!("SCardConnect failed, rc={}", rc); - } else { - // at this point, card should not equal state->card, - // to allow _ykpiv_connect() to determine device type - if _ykpiv_connect(state, state.context as (usize), card as (usize)).is_err() { - break; - } - } - - reader_ptr = reader_ptr.add(strlen(reader_ptr) + 1); - } - - if *reader_ptr == b'\0' as c_char { - error!("error: no usable reader found"); - SCardReleaseContext(state.context); - state.context = -1; - return Err(Error::PcscError); - } - - // Select applet. This is done here instead of in _ykpiv_connect() because - // you may not want to select the applet when connecting to a card handle that - // was supplied by an external library. - _ykpiv_begin_transaction(state)?; - - let res = _ykpiv_select_application(state); - let _ = _ykpiv_end_transaction(state); - res -} - -/// List readers -pub unsafe fn ykpiv_list_readers( - state: &mut YubiKey, - readers: *mut c_char, - len: *mut usize, -) -> Result<(), Error> { - let mut num_readers: u32 = 0u32; - let mut rc: i32; - - if SCardIsValidContext(state.context) != 0 { - rc = SCardEstablishContext(0x2, ptr::null(), ptr::null(), &mut state.context); - - if rc != 0 { - error!("error: SCardEstablishContext failed, rc={}", rc); - return Err(Error::PcscError); - } - } - - rc = SCardListReaders( - state.context, - ptr::null(), - ptr::null_mut(), - &mut num_readers, - ); - - if rc != 0 { - error!("error: SCardListReaders failed, rc={}", rc); - SCardReleaseContext(state.context); - state.context = -1i32; - return Err(Error::PcscError); - } - - if num_readers as (usize) > *len { - num_readers = *len as (u32); - } else if num_readers as (usize) < *len { - *len = num_readers as (usize); - } - - rc = SCardListReaders(state.context, ptr::null(), readers, &mut num_readers); - - if rc != 0 { - error!("error: SCardListReaders failed, rc={}", rc); - SCardReleaseContext(state.context); - state.context = -1i32; - return Err(Error::PcscError); - } - - *len = num_readers as usize; - Ok(()) -} - -/// Reconnect to a YubiKey -pub(crate) unsafe fn reconnect(state: &mut YubiKey) -> Result<(), Error> { - info!("trying to reconnect to current reader"); - - let mut active_protocol: u32 = 0; - let rc = SCardReconnect(state.card, 0x2u32, 0x2u32, 0x1u32, &mut active_protocol); - - if rc != 0x0 { - error!("SCardReconnect failed, rc={}", rc); - return Err(Error::PcscError); - } - - _ykpiv_select_application(state)?; - - if !state.pin.is_null() { - ykpiv_verify(state, state.pin as *const c_char).map(|_| ()) - } else { - Ok(()) - } -} - -/// Begin a transaction -pub(crate) unsafe fn _ykpiv_begin_transaction(state: &mut YubiKey) -> Result<(), Error> { - let mut rc = SCardBeginTransaction(state.card); - - if rc as usize & 0xffff_ffff == 0x8010_0068 { - reconnect(state)?; - rc = SCardBeginTransaction(state.card); - } - - if rc != 0 { - error!("failed to begin pcsc transaction, rc={}", rc); - return Err(Error::PcscError); - } - - Ok(()) -} - -/// End a transaction -pub(crate) unsafe fn _ykpiv_end_transaction(state: &mut YubiKey) -> Result<(), Error> { - let rc = SCardEndTransaction(state.card, 0x0); - - if rc != 0x0 { - error!("failed to end pcsc transaction, rc={}", rc); - return Err(Error::PcscError); - } - - Ok(()) -} - -/// Transfer data -pub(crate) unsafe fn _ykpiv_transfer_data( - state: &mut YubiKey, - templ: *const u8, - in_data: *const u8, - in_len: isize, - mut out_data: *mut u8, - out_len: *mut usize, - sw: *mut i32, -) -> Result<(), Error> { - let mut _currentBlock; - let mut in_ptr: *const u8 = in_data; - let max_out = *out_len; - let mut res: Result<(), Error>; - let mut recv_len: u32; - - *out_len = 0; - - loop { - let mut this_size: usize = 0xff; - let mut data = [0u8; 261]; - recv_len = data.len() as u32; - - let mut apdu = APDU::default(); - apdu.cla = *templ; - apdu.ins = *templ.offset(1); - apdu.p1 = *templ.offset(2); - apdu.p2 = *templ.offset(3); - - if in_ptr.offset(0xff) < in_data.offset(in_len) { - apdu.cla = 0x10; - } else { - this_size = in_data.offset(in_len) as usize - in_ptr as usize; - } - - trace!("going to send {} bytes in this go", this_size); - - apdu.lc = this_size.try_into().unwrap(); - memcpy( - apdu.data.as_mut_ptr() as *mut c_void, - in_ptr as *const c_void, - this_size, - ); - - res = _send_data(state, &mut apdu, data.as_mut_ptr(), &mut recv_len, sw); - - if res.is_err() { - _currentBlock = 24; - break; - } - if *sw != SW_SUCCESS && (*sw >> 8 != 0x61) { - _currentBlock = 24; - break; - } - if (*out_len) - .wrapping_add(recv_len as (usize)) - .wrapping_sub(2usize) - > max_out - { - _currentBlock = 21; - break; - } - - if !out_data.is_null() { - memcpy( - out_data as (*mut c_void), - data.as_mut_ptr() as (*const c_void), - recv_len.wrapping_sub(2u32) as (usize), - ); - out_data = out_data.offset(recv_len.wrapping_sub(2u32) as (isize)); - *out_len = (*out_len).wrapping_add(recv_len.wrapping_sub(2u32) as (usize)); - } - - in_ptr = in_ptr.add(this_size); - - if in_ptr >= in_data.offset(in_len) { - _currentBlock = 10; - break; - } - } - - if _currentBlock == 10 { - loop { - if *sw >> 8 != 0x61 { - _currentBlock = 24; - break; - } - - let mut data = [0u8; 261]; - recv_len = data.len() as u32; - - trace!( - "The card indicates there is {} bytes more data for us.", - *sw & 0xff - ); - - let mut apdu = APDU::default(); - apdu.ins = YKPIV_INS_GET_RESPONSE_APDU; - res = _send_data(state, &mut apdu, data.as_mut_ptr(), &mut recv_len, sw); - - if res.is_err() { - _currentBlock = 24; - break; - } - if *sw != SW_SUCCESS && (*sw >> 8 != 0x61) { - _currentBlock = 24; - break; - } - if (*out_len).wrapping_add(recv_len as (usize)).wrapping_sub(2) > max_out { - _currentBlock = 18; - break; - } - - if out_data.is_null() { - continue; - } - - memcpy( - out_data as (*mut c_void), - data.as_mut_ptr() as (*const c_void), - recv_len.wrapping_sub(2) as (usize), - ); - - out_data = out_data.offset(recv_len.wrapping_sub(2) as (isize)); - *out_len = (*out_len).wrapping_add(recv_len.wrapping_sub(2) as (usize)); - } - - if _currentBlock != 24 { - error!( - "Output buffer to small, wanted to write {}, max was {}.", - (*out_len).wrapping_add(recv_len as usize).wrapping_sub(2), - max_out - ); - - return Err(Error::SizeError); - } - } else if _currentBlock == 21 { - error!( - "Output buffer to small, wanted to write {}, max was {}.", - (*out_len).wrapping_add(recv_len as usize).wrapping_sub(2), - max_out - ); - - return Err(Error::SizeError); - } - - res -} - -/// Transfer data -pub unsafe fn ykpiv_transfer_data( - state: &mut YubiKey, - templ: *const u8, - in_data: *const u8, - in_len: isize, - out_data: *mut u8, - out_len: *mut usize, - sw: *mut i32, -) -> Result<(), Error> { - if let Err(e) = _ykpiv_begin_transaction(state) { - *out_len = 0; - return Err(e); - } - - let res = _ykpiv_transfer_data(state, templ, in_data, in_len, out_data, out_len, sw); - let _ = _ykpiv_end_transaction(state); - res -} - -/// Send data -pub(crate) unsafe fn _send_data( - state: &mut YubiKey, - apdu: &mut APDU, - data: *mut u8, - recv_len: *mut u32, - sw: *mut i32, -) -> Result<(), Error> { - let send_len = apdu.lc as u32 + 5; - let mut tmp_len = *recv_len; - - trace!("> {:?}", apdu); - - let rc = SCardTransmit( - state.card, - SCARD_PCI_T1, - apdu.as_mut_ptr() as *mut i8, - send_len, - ptr::null(), - data, - &mut tmp_len, - ); - - if rc != SCARD_S_SUCCESS { - error!("error: SCardTransmit failed, rc={:08x}", rc); - return Err(Error::PcscError); - } - - *recv_len = tmp_len; - trace!("< {:?}", slice::from_raw_parts(data, *recv_len as usize)); - - if *recv_len >= 2 { - *sw = *data.offset((*recv_len).wrapping_sub(2) as (isize)) as (i32) << 8 - | *data.offset((*recv_len).wrapping_sub(1) as (isize)) as (i32); - } else { - *sw = 0; - } - - Ok(()) -} - -/// Default authentication key -pub const DEFAULT_AUTH_KEY: &[u8; DES_LEN_3DES] = b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08"; - -/// Authenticate to the card -pub unsafe fn ykpiv_authenticate( - state: &mut YubiKey, - key: Option<&[u8; DES_LEN_3DES]>, -) -> Result<(), Error> { - let mut data = [0u8; 261]; - let mut recv_len = data.len() as u32; - let mut sw: i32 = 0; - let mut res = Ok(()); - - _ykpiv_begin_transaction(state)?; - - if _ykpiv_ensure_application_selected(state).is_ok() { - // use the provided mgm key to authenticate; if it hasn't been provided, use default - let mgm_key = DesKey::from_bytes(*key.unwrap_or(DEFAULT_AUTH_KEY)); - - // get a challenge from the card - let mut apdu = APDU::default(); - apdu.ins = YKPIV_INS_AUTHENTICATE; - apdu.p1 = YKPIV_ALGO_3DES; // triple des - apdu.p2 = YKPIV_KEY_CARDMGM; // management key - apdu.lc = 0x04; - apdu.data[0] = 0x7c; - apdu.data[1] = 0x02; - apdu.data[2] = 0x80; - - res = _send_data(state, &mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw); - - if res.is_err() { - let _ = _ykpiv_end_transaction(state); - return res; - } else if sw != SW_SUCCESS { - let _ = _ykpiv_end_transaction(state); - return Err(Error::AuthenticationError); - } - - let mut challenge = [0u8; 8]; - challenge.copy_from_slice(&data[4..12]); - - // send a response to the cards challenge and a challenge of our own. - let mut response = [0u8; 8]; - des_decrypt(&mgm_key, &challenge, &mut response); - - recv_len = data.len() as u32; - apdu = APDU::default(); - apdu.ins = YKPIV_INS_AUTHENTICATE; - apdu.p1 = YKPIV_ALGO_3DES; // triple des - apdu.p2 = YKPIV_KEY_CARDMGM; // management key - apdu.data[0] = 0x7c; - apdu.data[1] = 20; // 2 + 8 + 2 +8 - apdu.data[2] = 0x80; - apdu.data[3] = 8; - apdu.data[4..12].copy_from_slice(&response); - apdu.data[12] = 0x81; - apdu.data[13] = 8; - - if getrandom(&mut data[14..22]).is_err() { - error!("failed getting randomness for authentication."); - let _ = _ykpiv_end_transaction(state); - return Err(Error::RandomnessError); - } - challenge.copy_from_slice(&data[14..22]); - - apdu.lc = 22; - - res = _send_data(state, &mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw); - - if res.is_err() { - let _ = _ykpiv_end_transaction(state); - return res; - } else if sw != SW_SUCCESS { - let _ = _ykpiv_end_transaction(state); - return Err(Error::AuthenticationError); - } - - // compare the response from the card with our challenge - des_encrypt(&mgm_key, &challenge, &mut response); - - // TODO(tarcieri): constant time comparison! - if response == data[4..12] { - res = Ok(()); - } else { - res = Err(Error::AuthenticationError); - } - } - - let _ = _ykpiv_end_transaction(state); - res -} - -/// Set the management key (MGM) -pub unsafe fn ykpiv_set_mgmkey( - state: &mut YubiKey, - new_key: &[u8; DES_LEN_3DES], -) -> Result<(), Error> { - ykpiv_set_mgmkey2(state, new_key, 0) -} - -/// Set the management key (MGM) -pub(crate) unsafe fn ykpiv_set_mgmkey2( - state: &mut YubiKey, - new_key: &[u8; DES_LEN_3DES], - touch: u8, -) -> Result<(), Error> { - let mut data = [0u8; 261]; - let mut recv_len = data.len() as u32; - let mut sw: i32 = 0; - let mut res = Ok(()); - let mut apdu = APDU::default(); - - _ykpiv_begin_transaction(state)?; - - if _ykpiv_ensure_application_selected(state).is_ok() { - if yk_des_is_weak_key(new_key) { - error!( - "won't set new key '{:?}' since it's weak (with odd parity)", - new_key - ); - res = Err(Error::KeyError); - } else { - apdu.ins = YKPIV_INS_SET_MGMKEY; - apdu.p1 = 0xff; - - apdu.p2 = match touch { - 0 => 0xff, - 1 => 0xfe, - _ => { - let _ = _ykpiv_end_transaction(state); - return Err(Error::GenericError); - } - }; - - apdu.lc = DES_LEN_3DES as u8 + 3; - apdu.data[0] = YKPIV_ALGO_3DES; - apdu.data[1] = YKPIV_KEY_CARDMGM; - apdu.data[2] = DES_LEN_3DES as u8; - apdu.data[3..3 + DES_LEN_3DES].copy_from_slice(new_key); - - res = _send_data(state, &mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw); - - if res.is_ok() && sw != SW_SUCCESS { - res = Err(Error::GenericError); - } - } - } - - apdu.zeroize(); - let _ = _ykpiv_end_transaction(state); - res -} - -/// Authenticate to the YubiKey -pub(crate) unsafe fn _general_authenticate( - state: &mut YubiKey, - sign_in: *const u8, - in_len: usize, - out: *mut u8, - out_len: *mut usize, - algorithm: u8, - key: u8, - decipher: bool, -) -> Result<(), Error> { - let mut _currentBlock; - let mut indata = [0u8; 1024]; - let mut dataptr: *mut u8 = indata.as_mut_ptr(); - let mut data = [0u8; 1024]; - let templ = [0, YKPIV_INS_AUTHENTICATE, algorithm, key]; - let mut recv_len = data.len(); - let mut sw: i32 = 0; - let bytes: usize; - let mut len: usize = 0; - - match algorithm { - YKPIV_ALGO_RSA1024 | YKPIV_ALGO_RSA2048 => { - let key_len = if algorithm == YKPIV_ALGO_RSA1024 { - 128 - } else { - 256 - }; - - if in_len != key_len { - return Err(Error::SizeError); - } else { - _currentBlock = 16; - } - } - YKPIV_ALGO_ECCP256 | YKPIV_ALGO_ECCP384 => { - let key_len = if algorithm == YKPIV_ALGO_ECCP256 { - 32 - } else { - 48 - }; - - if (!decipher && (in_len > key_len)) || (decipher && (in_len != (key_len * 2) + 1)) { - return Err(Error::SizeError); - } - } - _ => return Err(Error::AlgorithmError), - } - - if in_len < 0x80 { - bytes = 1; - } else if in_len < 0xff { - bytes = 2; - } else { - bytes = 3; - } - - *dataptr = 0x7c; - dataptr = dataptr.add(_ykpiv_set_length(dataptr, in_len + bytes + 3)); - *dataptr = 0x82; - *dataptr.add(1) = 0x00; - *dataptr.add(2) = - if (algorithm == YKPIV_ALGO_ECCP256 || algorithm == YKPIV_ALGO_ECCP384) && decipher { - 0x85 - } else { - 0x81 - }; - dataptr = dataptr.add(3 + _ykpiv_set_length(dataptr, in_len)); - memcpy(dataptr as *mut c_void, sign_in as *const c_void, in_len); - dataptr = dataptr.add(in_len); - - if let Err(e) = ykpiv_transfer_data( - state, - templ.as_ptr(), - indata.as_mut_ptr(), - dataptr as isize - indata.as_mut_ptr() as isize, - data.as_mut_ptr(), - &mut recv_len, - &mut sw, - ) { - error!("sign command failed to communicate"); - return Err(e); - } - - if sw != SW_SUCCESS { - error!("Failed sign command with code {:x}", sw); - - if sw == SW_ERR_SECURITY_STATUS { - return Err(Error::AuthenticationError); - } else { - return Err(Error::GenericError); - } - } - - // skip the first 7c tag - if data[0] != 0x7c { - error!("failed parsing signature reply (0x7c byte)"); - return Err(Error::ParseError); - } - - dataptr = data.as_mut_ptr().add(1); - dataptr = dataptr.add(_ykpiv_get_length(dataptr, &mut len)); - - // skip the 82 tag - if *dataptr != 0x82 { - error!("failed parsing signature reply (0x82 byte)"); - return Err(Error::ParseError); - } - - dataptr = dataptr.add(1); - dataptr = dataptr.add(_ykpiv_get_length(dataptr, &mut len)); - - if len > *out_len { - error!("wrong size on output buffer"); - return Err(Error::SizeError); - } - - *out_len = len; - memcpy(out as (*mut c_void), dataptr as (*const c_void), len); - Ok(()) -} - -/// Sign data using a PIV key -pub unsafe fn ykpiv_sign_data( - state: &mut YubiKey, - raw_in: *const u8, - in_len: usize, - sign_out: *mut u8, - out_len: *mut usize, - algorithm: u8, - key: u8, -) -> Result<(), Error> { - _ykpiv_begin_transaction(state)?; - - // don't attempt to reselect in crypt operations to avoid problems with PIN_ALWAYS - - let res = _general_authenticate( - state, raw_in, in_len, sign_out, out_len, algorithm, key, false, - ); - - let _ = _ykpiv_end_transaction(state); - res -} - -/// Decrypt data using a PIV key -pub unsafe fn ykpiv_decrypt_data( - state: &mut YubiKey, - input: *const u8, - input_len: usize, - out: *mut u8, - out_len: *mut usize, - algorithm: u8, - key: u8, -) -> Result<(), Error> { - _ykpiv_begin_transaction(state)?; - - // don't attempt to reselect in crypt operations to avoid problems with PIN_ALWAYS - - let res = _general_authenticate(state, input, input_len, out, out_len, algorithm, key, true); - let _ = _ykpiv_end_transaction(state); - res -} - -/// Get the version of the PIV application installed on the YubiKey -pub(crate) unsafe fn _ykpiv_get_version(state: &mut YubiKey) -> Result { - let mut data = [0u8; 261]; - let mut recv_len = data.len() as u32; - let mut sw: i32 = 0; - - // get version from state if already from device - if state.ver.major != 0 || state.ver.minor != 0 || state.ver.patch != 0 { - return Ok(state.ver); - } - - // get version from device - let mut apdu = APDU::default(); - apdu.ins = YKPIV_INS_GET_VERSION; - - _send_data(state, &mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw)?; - - if sw != SW_SUCCESS { - return Err(Error::GenericError); - } - - if recv_len < 3 { - return Err(Error::SizeError); - } - - state.ver.major = data[0]; - state.ver.minor = data[1]; - state.ver.patch = data[2]; - - Ok(state.ver) -} - -/// Get the YubiKey's PIV application version as a string -pub unsafe fn ykpiv_get_version(state: &mut YubiKey) -> Result { - let mut res = Err(Error::GenericError); - - _ykpiv_begin_transaction(state)?; - - if _ykpiv_ensure_application_selected(state).is_ok() { - res = _ykpiv_get_version(state); - } - - let _ = _ykpiv_end_transaction(state); - res.map(|ver| format!("{}.{}.{}", ver.major, ver.minor, ver.patch)) -} - -/// Get YubiKey device serial number -/// -/// NOTE: caller must make sure that this is wrapped in a transaction for synchronized operation -pub(crate) unsafe fn _ykpiv_get_serial(state: &mut YubiKey, f_force: bool) -> Result { - let yk_applet: *const u8 = ptr::null(); - let mut data = [0u8; 255]; - let mut recv_len = data.len() as u32; - let mut sw: i32 = 0; - let p_temp: *mut u8; - - if !f_force && state.serial != 0 { - return Ok(state.serial); - } - - if state.ver.major < 5 { - // get serial from neo/yk4 devices using the otp applet - let mut temp = [0u8; 255]; - recv_len = temp.len() as u32; - - let mut apdu = APDU::default(); - apdu.ins = YKPIV_INS_SELECT_APPLICATION; - apdu.p1 = 0x04; - apdu.lc = mem::size_of_val(&yk_applet) as u8; - - memcpy( - apdu.data.as_mut_ptr() as *mut c_void, - yk_applet as *const c_void, - mem::size_of_val(&yk_applet), - ); - - if let Err(e) = _send_data(state, &mut apdu, temp.as_mut_ptr(), &mut recv_len, &mut sw) { - error!("failed communicating with card: '{}'", e); - return Err(e); - } - - if sw != SW_SUCCESS { - error!("failed selecting yk application: {:04x}", sw); - return Err(Error::GenericError); - } - - recv_len = temp.len() as u32; - apdu = APDU::default(); - apdu.ins = 0x01; - apdu.p1 = 0x10; - apdu.lc = 0x00; - - if let Err(e) = _send_data(state, &mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw) { - error!("failed communicating with card: '{}'", e); - return Err(e); - } - - if sw != SW_SUCCESS { - error!("failed retrieving serial number: {:04x}", sw); - return Err(Error::GenericError); - } - - recv_len = temp.len() as u32; - apdu = APDU::default(); - apdu.ins = YKPIV_INS_SELECT_APPLICATION; - apdu.p1 = 0x04; - apdu.lc = mem::size_of_val(&AID) as u8; - - memcpy( - apdu.data.as_mut_ptr() as *mut c_void, - AID.as_ptr() as *const c_void, - mem::size_of_val(&AID), - ); - - if let Err(e) = _send_data(state, &mut apdu, temp.as_mut_ptr(), &mut recv_len, &mut sw) { - error!("failed communicating with card: '{}'", e); - return Err(e); - } - - if sw != SW_SUCCESS { - error!("failed selecting application: {:04x}", sw); - return Err(Error::GenericError); - } - } else { - // get serial from yk5 and later devices using the f8 command - let mut apdu = APDU::default(); - apdu.ins = YKPIV_INS_GET_SERIAL; - - if let Err(e) = _send_data(state, &mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw) { - error!("failed communicating with card: '{}'", e); - return Err(e); - } - - if sw != SW_SUCCESS { - error!("failed retrieving serial number: {:04x}", sw); - return Err(Error::GenericError); - } - } - - // check that we received enough data for the serial number - if recv_len < 4 { - return Err(Error::SizeError); - } - - // TODO(tarcieri): replace pointers and casts with proper references! - #[allow(trivial_casts)] - { - p_temp = &mut state.serial as (*mut u32) as (*mut u8); - } - - *p_temp = data[3]; - *p_temp.add(1) = data[2]; - *p_temp.add(2) = data[1]; - *p_temp.add(3) = data[0]; - - Ok(state.serial) -} - -/// Get YubiKey device serial number -pub unsafe fn ykpiv_get_serial(state: &mut YubiKey) -> Result { - let mut res = Err(Error::GenericError); - - _ykpiv_begin_transaction(state)?; - - if _ykpiv_ensure_application_selected(state).is_ok() { - res = _ykpiv_get_serial(state, false); - } - - let _ = _ykpiv_end_transaction(state); - res -} - -/// Cache PIN in memory -// TODO(tarcieri): better security around the cached PIN -pub(crate) unsafe fn _cache_pin( - state: &mut YubiKey, - pin: *const c_char, - len: usize, -) -> Result<(), Error> { - if !pin.is_null() && (state.pin as *const c_char == pin) { - return Ok(()); - } - - if !state.pin.is_null() { - // Zeroize the old cached PIN - let old_len = strlen(state.pin as *const c_char); - let pin_slice = slice::from_raw_parts_mut(state.pin, old_len); - pin_slice.zeroize(); - - free(state.pin as (*mut c_void)); - state.pin = ptr::null_mut(); - } - - if !pin.is_null() && len > 0 { - state.pin = malloc(len + 1) as (*mut u8); - - if state.pin.is_null() { - return Err(Error::MemoryError); - } - - memcpy(state.pin as (*mut c_void), pin as (*const c_void), len); - *state.pin.add(len) = 0u8; - } - - Ok(()) -} - -/// Verify device PIN -/// -/// Returns the number of tries remaining both on success and on a wrong PIN. -pub unsafe fn ykpiv_verify(state: &mut YubiKey, pin: *const c_char) -> Result { - ykpiv_verify_select( - state, - pin, - if !pin.is_null() { strlen(pin) } else { 0 }, - false, - ) -} - -/// Verify device PIN -/// -/// Returns the number of tries remaining both on success and on a wrong PIN. -pub(crate) unsafe fn _verify( - state: &mut YubiKey, - pin: *const c_char, - pin_len: usize, -) -> Result { - let mut data = [0u8; 261]; - let mut recv_len = data.len() as u32; - let mut sw: i32 = 0; - - if pin_len > CB_PIN_MAX { - return Err(Error::SizeError); - } - - let mut apdu = APDU::default(); - apdu.ins = YKPIV_INS_VERIFY; - apdu.p1 = 0x00; - apdu.p2 = 0x80; - apdu.lc = if pin.is_null() { 0 } else { 0x08 }; - - if !pin.is_null() { - memcpy( - apdu.data.as_mut_ptr() as *mut c_void, - pin as *const c_void, - pin_len, - ); - - if pin_len < CB_PIN_MAX { - memset( - apdu.data.as_mut_ptr().add(pin_len) as *mut c_void, - 0xff, - CB_PIN_MAX - pin_len, - ); - } - } - - let res = _send_data(state, &mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw); - - apdu.zeroize(); - - if let Err(e) = res { - return Err(e); - } - - if sw == SW_SUCCESS { - if !pin.is_null() && (pin_len != 0) { - // Intentionally ignore errors. If the PIN fails to save, it will only - // be a problem if a reconnect is attempted. Failure deferred until then. - let _ = _cache_pin(state, pin, pin_len); - } - - Ok(sw & 0xf) - } else if sw >> 8 == 0x63 { - Err(Error::WrongPin { tries: sw & 0xf }) - } else if sw == SW_ERR_AUTH_BLOCKED { - Err(Error::WrongPin { tries: 0 }) - } else { - Err(Error::GenericError) - } -} - -/// Verify and select application -/// -/// Returns the number of tries remaining both on success and on a wrong PIN. -pub unsafe fn ykpiv_verify_select( - state: &mut YubiKey, - pin: *const c_char, - pin_len: usize, - force_select: bool, -) -> Result { - let mut res = Ok(-1); - - _ykpiv_begin_transaction(state)?; - - if force_select { - if let Err(e) = _ykpiv_ensure_application_selected(state) { - res = Err(e); - } - } - - if res.is_ok() { - res = _verify(state, pin, pin_len); - } - - let _ = _ykpiv_end_transaction(state); - res -} - -/// Get the number of PIN retries -pub unsafe fn ykpiv_get_pin_retries(state: &mut YubiKey) -> Result { - // Force a re-select to unverify, because once verified the spec dictates that - // subsequent verify calls will return a "verification not needed" instead of - // the number of tries left... - _ykpiv_select_application(state)?; - - let ykrc = ykpiv_verify(state, ptr::null()); - - // WRONG_PIN is expected on successful query. - match ykrc { - Ok(tries) | Err(Error::WrongPin { tries }) => Ok(tries), - Err(e) => Err(e), - } -} - -/// Set the number of PIN retries -pub unsafe fn ykpiv_set_pin_retries( - state: &mut YubiKey, - pin_tries: i32, - puk_tries: i32, -) -> Result<(), Error> { - let mut res = Ok(()); - let mut templ = [0, YKPIV_INS_SET_PIN_RETRIES, 0, 0]; - let mut data = [0u8; 255]; - let mut recv_len: usize = data.len(); - let mut sw: i32 = 0i32; - - // Special case: if either retry count is 0, it's a successful no-op - if pin_tries == 0 || puk_tries == 0 { - return Ok(()); - } - - if pin_tries > 0xff || puk_tries > 0xff || pin_tries < 1 || puk_tries < 1 { - return Err(Error::RangeError); - } - - templ[2] = pin_tries as (u8); - templ[3] = puk_tries as (u8); - - _ykpiv_begin_transaction(state)?; - - if _ykpiv_ensure_application_selected(state).is_ok() { - res = ykpiv_transfer_data( - state, - templ.as_ptr(), - ptr::null(), - 0, - data.as_mut_ptr(), - &mut recv_len, - &mut sw, - ); - - if res.is_ok() { - res = match sw { - SW_SUCCESS => Ok(()), - SW_ERR_AUTH_BLOCKED => Err(Error::AuthenticationError), - SW_ERR_SECURITY_STATUS => Err(Error::AuthenticationError), - _ => Err(Error::GenericError), - }; - } - } - - let _ = _ykpiv_end_transaction(state); - res -} - -/// Change the PIN -pub(crate) unsafe fn _ykpiv_change_pin( - state: &mut YubiKey, - action: i32, - current_pin: *const c_char, - current_pin_len: usize, - new_pin: *const c_char, - new_pin_len: usize, -) -> Result<(), Error> { - let mut sw: i32 = 0; - let mut templ = [0, YKPIV_INS_CHANGE_REFERENCE, 0, 0x80]; - let mut indata = [0u8; 16]; - let mut data = [0u8; 255]; - let mut recv_len: usize = data.len(); - - if current_pin_len > 8 || new_pin_len > 8 { - return Err(Error::SizeError); - } - - if action == CHREF_ACT_UNBLOCK_PIN { - templ[1] = YKPIV_INS_RESET_RETRY; - } else if action == CHREF_ACT_CHANGE_PUK { - templ[3] = 0x81; - } - - memcpy( - indata.as_mut_ptr() as (*mut c_void), - current_pin as (*const c_void), - current_pin_len, - ); - - if current_pin_len < 8 { - memset( - indata.as_mut_ptr().add(current_pin_len) as *mut c_void, - 0xff, - 8 - current_pin_len, - ); - } - - memcpy( - indata.as_mut_ptr().offset(8) as *mut c_void, - new_pin as *const c_void, - new_pin_len, - ); - - if new_pin_len < 8 { - memset( - indata.as_mut_ptr().offset(8).add(new_pin_len) as *mut c_void, - 0xff, - 8 - new_pin_len, - ); - } - - let res = ykpiv_transfer_data( - state, - templ.as_ptr(), - indata.as_mut_ptr(), - indata.len() as isize, - data.as_mut_ptr(), - &mut recv_len, - &mut sw, - ); - - indata.zeroize(); - - if res.is_err() { - return res; - } - - if sw != SW_SUCCESS { - if sw >> 8 == 0x63 { - return Err(Error::WrongPin { tries: sw & 0xf }); - } - - if sw == SW_ERR_AUTH_BLOCKED { - return Err(Error::PinLocked); - } - - error!("failed changing pin, token response code: {:x}.", sw); - return Err(Error::GenericError); - } - - Ok(()) -} - -/// Change the Personal Identification Number (PIN). -/// -/// The default PIN code is 123456 -pub unsafe fn ykpiv_change_pin( - state: &mut YubiKey, - current_pin: *const c_char, - current_pin_len: usize, - new_pin: *const c_char, - new_pin_len: usize, -) -> Result<(), Error> { - let mut res = Err(Error::GenericError); - - _ykpiv_begin_transaction(state)?; - - if _ykpiv_ensure_application_selected(state).is_ok() { - res = _ykpiv_change_pin(state, 0, current_pin, current_pin_len, new_pin, new_pin_len); - - if res.is_ok() && !new_pin.is_null() { - // Intentionally ignore errors. If the PIN fails to save, it will only - // be a problem if a reconnect is attempted. Failure deferred until then. - let _ = _cache_pin(state, new_pin, new_pin_len); - } - } - - let _ = _ykpiv_end_transaction(state); - res -} - -/// Change the PIN Unblocking Key (PUK). PUKs are codes for resetting -/// lost/forgotten PINs, or devices that have become blocked because of too -/// many failed attempts. -/// -/// The PUK is part of the PIV standard that the YubiKey follows. -/// -/// The default PUK code is 12345678. -pub unsafe fn ykpiv_change_puk( - state: &mut YubiKey, - current_puk: *const c_char, - current_puk_len: usize, - new_puk: *const c_char, - new_puk_len: usize, -) -> Result<(), Error> { - let mut res = Err(Error::GenericError); - - _ykpiv_begin_transaction(state)?; - - if _ykpiv_ensure_application_selected(state).is_ok() { - res = _ykpiv_change_pin(state, 2, current_puk, current_puk_len, new_puk, new_puk_len); - } - - let _ = _ykpiv_end_transaction(state); - res -} - -/// Unblock a Personal Identification Number (PIN) using a previously -/// configured PIN Unblocking Key (PUK). -pub unsafe fn ykpiv_unblock_pin( - state: &mut YubiKey, - puk: *const c_char, - puk_len: usize, - new_pin: *const c_char, - new_pin_len: usize, -) -> Result<(), Error> { - let mut res = Err(Error::GenericError); - - _ykpiv_begin_transaction(state)?; - - if _ykpiv_ensure_application_selected(state).is_ok() { - res = _ykpiv_change_pin(state, 1, puk, puk_len, new_pin, new_pin_len); - } - - let _ = _ykpiv_end_transaction(state); - res -} - -/// Fetch an object from the YubiKey -pub unsafe fn ykpiv_fetch_object( - state: &mut YubiKey, - object_id: i32, - data: *mut u8, - len: *mut usize, -) -> Result<(), Error> { - let mut res = Ok(()); - - _ykpiv_begin_transaction(state)?; - - if _ykpiv_ensure_application_selected(state).is_ok() { - res = _ykpiv_fetch_object(state, object_id, data, len); - } - - let _ = _ykpiv_end_transaction(state); - res -} - -/// Fetch an object -pub(crate) unsafe fn _ykpiv_fetch_object( - state: &mut YubiKey, - object_id: i32, - data: *mut u8, - len: *mut usize, -) -> Result<(), Error> { - let mut sw: i32 = 0; - let mut indata = [0u8; 5]; - let mut inptr: *mut u8 = indata.as_mut_ptr(); - let templ = [0, YKPIV_INS_GET_DATA, 0x3f, 0xff]; - - inptr = set_object(object_id, inptr); - - if inptr.is_null() { - return Err(Error::InvalidObject); - } - - ykpiv_transfer_data( - state, - templ.as_ptr(), - indata.as_mut_ptr(), - inptr as isize - indata.as_mut_ptr() as isize, - data, - len, - &mut sw, - )?; - - if sw != SW_SUCCESS { - return Err(Error::GenericError); - } - - let mut outlen: usize = 0; - - if *len < 2 || !_ykpiv_has_valid_length(data.offset(1), (*len).wrapping_sub(1)) { - return Err(Error::SizeError); - } - - let offs = _ykpiv_get_length(data.offset(1), &mut outlen); - - if offs == 0 { - return Err(Error::SizeError); - } - - if outlen.wrapping_add(offs).wrapping_add(1) != *len { - error!( - "invalid length indicated in object: total len is {} but indicated length is {}", - *len, outlen - ); - - return Err(Error::SizeError); - } - - memmove( - data as *mut c_void, - data.add(1).add(offs) as *const c_void, - outlen, - ); - *len = outlen; - - Ok(()) -} - -/// Save an object -pub unsafe fn ykpiv_save_object( - state: &mut YubiKey, - object_id: i32, - indata: *mut u8, - len: usize, -) -> Result<(), Error> { - let mut res = Ok(()); - - _ykpiv_begin_transaction(state)?; - - if _ykpiv_ensure_application_selected(state).is_ok() { - res = _ykpiv_save_object(state, object_id, indata, len); - } - - let _ = _ykpiv_end_transaction(state); - res -} - -/// Save an object -pub unsafe fn _ykpiv_save_object( - state: &mut YubiKey, - object_id: i32, - indata: *mut u8, - len: usize, -) -> Result<(), Error> { - let mut data = [0u8; YKPIV_OBJ_MAX_SIZE]; - let mut dataptr: *mut u8 = data.as_mut_ptr(); - let templ = [0, YKPIV_INS_PUT_DATA, 0x3f, 0xff]; - let mut sw: i32 = 0; - let mut outlen: usize = 0usize; - - if len > CB_OBJ_MAX { - return Err(Error::SizeError); - } - - dataptr = set_object(object_id, dataptr); - - if dataptr.is_null() { - return Err(Error::InvalidObject); - } - *{ - let _old = dataptr; - dataptr = dataptr.offset(1); - _old - } = 0x53; - - dataptr = dataptr.add(_ykpiv_set_length(dataptr, len)); - memcpy(dataptr as (*mut c_void), indata as (*const c_void), len); - dataptr = dataptr.add(len); - - _ykpiv_transfer_data( - state, - templ.as_ptr(), - data.as_mut_ptr(), - dataptr as isize - data.as_mut_ptr() as isize, - ptr::null_mut(), - &mut outlen, - &mut sw, - )?; - - match sw { - SW_SUCCESS => Ok(()), - SW_ERR_SECURITY_STATUS => Err(Error::AuthenticationError), - _ => Err(Error::GenericError), - } -} - /// Set an object pub(crate) unsafe fn set_object(object_id: i32, mut buffer: *mut u8) -> *mut u8 { *buffer = 0x5c; @@ -1781,331 +2092,3 @@ pub(crate) unsafe fn set_object(object_id: i32, mut buffer: *mut u8) -> *mut u8 buffer } - -/// Import a private encryption or signing key into the YubiKey -pub unsafe fn ykpiv_import_private_key( - state: &mut YubiKey, - key: u8, - algorithm: u8, - p: *const u8, - p_len: usize, - q: *const u8, - q_len: usize, - dp: *const u8, - dp_len: usize, - dq: *const u8, - dq_len: usize, - qinv: *const u8, - qinv_len: usize, - ec_data: *const u8, - ec_data_len: u8, - pin_policy: u8, - touch_policy: u8, -) -> Result<(), Error> { - let mut key_data = [0u8; 1024]; - let mut in_ptr: *mut u8 = key_data.as_mut_ptr(); - let templ = [0, YKPIV_INS_IMPORT_KEY, algorithm, key]; - let mut data = [0u8; 256]; - let mut recv_len = data.len(); - let mut elem_len: u32 = 0; - let mut sw: i32 = 0; - let mut params: [*const u8; 5] = [ptr::null(); 5]; - let mut lens = [0usize; 5]; - let n_params: u8; - let param_tag: i32; - - if key == YKPIV_KEY_CARDMGM - || key < YKPIV_KEY_RETIRED1 - || key > YKPIV_KEY_RETIRED20 && (key < YKPIV_KEY_AUTHENTICATION) - || key > YKPIV_KEY_CARDAUTH && (key != YKPIV_KEY_ATTESTATION) - { - return Err(Error::KeyError); - } - - if pin_policy != YKPIV_PINPOLICY_DEFAULT - && (pin_policy != YKPIV_PINPOLICY_NEVER) - && (pin_policy != YKPIV_PINPOLICY_ONCE) - && (pin_policy != YKPIV_PINPOLICY_ALWAYS) - { - return Err(Error::GenericError); - } - - if touch_policy != YKPIV_TOUCHPOLICY_DEFAULT - && (touch_policy != YKPIV_TOUCHPOLICY_NEVER) - && (touch_policy != YKPIV_TOUCHPOLICY_ALWAYS) - && (touch_policy != YKPIV_TOUCHPOLICY_CACHED) - { - return Err(Error::GenericError); - } - - match algorithm { - YKPIV_ALGO_RSA1024 | YKPIV_ALGO_RSA2048 => { - if p_len + q_len + dp_len + dq_len + qinv_len >= 1024 { - return Err(Error::SizeError); - } else { - if algorithm == YKPIV_ALGO_RSA1024 { - elem_len = 64; - } - - if algorithm == YKPIV_ALGO_RSA2048 { - elem_len = 128; - } - - if p.is_null() || q.is_null() || dp.is_null() || dq.is_null() || qinv.is_null() { - return Err(Error::GenericError); - } - - params[0] = p; - lens[0] = p_len; - params[1] = q; - lens[1] = q_len; - params[2] = dp; - lens[2] = dp_len; - params[3] = dq; - lens[3] = dq_len; - params[4] = qinv; - lens[4] = qinv_len; - param_tag = 0x1; - n_params = 5u8; - } - } - YKPIV_ALGO_ECCP256 | YKPIV_ALGO_ECCP384 => { - if ec_data_len as (usize) >= key_data.len() { - return Err(Error::SizeError); - } - - if algorithm == YKPIV_ALGO_ECCP256 { - elem_len = 32; - } else if algorithm == YKPIV_ALGO_ECCP384 { - elem_len = 48; - } - - if ec_data.is_null() { - return Err(Error::GenericError); - } - - params[0] = ec_data; - lens[0] = ec_data_len as usize; - param_tag = 0x6; - n_params = 1; - } - _ => return Err(Error::AlgorithmError), - } - - for i in 0..n_params { - *in_ptr = (param_tag + i as i32) as u8; - in_ptr = in_ptr.offset(1); - - in_ptr = in_ptr.add(_ykpiv_set_length(in_ptr, elem_len as usize)); - let padding = (elem_len as (usize)).wrapping_sub(lens[i as usize]); - let remaining = (key_data.as_mut_ptr() as usize) + 1024 - in_ptr as usize; - - if padding > remaining { - return Err(Error::AlgorithmError); - } - - memset(in_ptr as *mut c_void, 0, padding); - in_ptr = in_ptr.add(padding); - memcpy( - in_ptr as *mut c_void, - params[i as usize] as *const c_void, - lens[i as usize], - ); - in_ptr = in_ptr.add(lens[i as usize]); - } - - if pin_policy != YKPIV_PINPOLICY_DEFAULT { - *in_ptr = YKPIV_PINPOLICY_TAG; - *in_ptr.add(1) = 0x01; - *in_ptr.add(2) = pin_policy; - in_ptr = in_ptr.add(3); - } - - if touch_policy != YKPIV_TOUCHPOLICY_DEFAULT { - *in_ptr = YKPIV_TOUCHPOLICY_TAG; - *in_ptr.add(1) = 0x01; - *in_ptr.add(2) = touch_policy; - in_ptr = in_ptr.add(3); - } - - _ykpiv_begin_transaction(state)?; - - let mut res = Ok(()); - if _ykpiv_ensure_application_selected(state).is_ok() { - res = ykpiv_transfer_data( - state, - templ.as_ptr(), - key_data.as_mut_ptr(), - in_ptr as isize - key_data.as_mut_ptr() as isize, - data.as_mut_ptr(), - &mut recv_len, - &mut sw, - ); - - if res.is_ok() && sw != SW_SUCCESS { - res = Err(Error::GenericError); - if sw == SW_ERR_SECURITY_STATUS { - res = Err(Error::AuthenticationError); - } - } - } - - key_data.zeroize(); - let _ = _ykpiv_end_transaction(state); - res -} - -/// Generate an attestation certificate for a stored key -pub unsafe fn ykpiv_attest( - state: &mut YubiKey, - key: u8, - data: *mut u8, - data_len: *mut usize, -) -> Result<(), Error> { - let mut res = Err(Error::GenericError); - let templ = [0, YKPIV_INS_ATTEST, key, 0]; - let mut sw: i32 = 0; - let mut ul_data_len: usize; - - if data.is_null() || data_len.is_null() { - return Err(Error::ArgumentError); - } - - ul_data_len = *data_len; - - _ykpiv_begin_transaction(state)?; - - if _ykpiv_ensure_application_selected(state).is_ok() { - res = ykpiv_transfer_data( - state, - templ.as_ptr(), - ptr::null(), - 0, - data, - &mut ul_data_len, - &mut sw, - ); - - if res.is_ok() { - if sw != SW_SUCCESS { - res = Err(Error::GenericError); - if sw == SW_ERR_NOT_SUPPORTED { - res = Err(Error::NotSupported); - } - } else if *data as i32 != 0x30 { - res = Err(Error::GenericError); - } else { - *data_len = ul_data_len; - } - } - } - - let _ = _ykpiv_end_transaction(state); - res -} - -/// Get an auth challenge -pub unsafe fn ykpiv_auth_getchallenge(state: &mut YubiKey) -> Result<[u8; 8], Error> { - let mut data = [0u8; 261]; - let mut recv_len = data.len() as u32; - let mut sw: i32 = 0; - // TODO(str4d): What should the default value be if the application is not selected? - let mut res = Ok([0; 8]); - - _ykpiv_begin_transaction(state)?; - - if _ykpiv_ensure_application_selected(state).is_ok() { - let mut apdu = APDU::default(); - apdu.ins = YKPIV_INS_AUTHENTICATE; - apdu.p1 = YKPIV_ALGO_3DES; // triple des - apdu.p2 = YKPIV_KEY_CARDMGM; // management key - apdu.lc = 0x04; - apdu.data[0] = 0x7c; - apdu.data[1] = 0x02; - apdu.data[2] = 0x81; //0x80; - - if let Err(e) = _send_data(state, &mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw) { - res = Err(e) - } else if sw != SW_SUCCESS { - res = Err(Error::AuthenticationError); - } else { - let mut challenge = [0; 8]; - challenge.copy_from_slice(&data[4..12]); - res = Ok(challenge); - } - } - - let _ = _ykpiv_end_transaction(state); - res -} - -/// Verify an auth response -pub unsafe fn ykpiv_auth_verifyresponse( - state: &mut YubiKey, - response: [u8; 8], -) -> Result<(), Error> { - let mut data = [0u8; 261]; - let mut recv_len = data.len() as u32; - let mut sw: i32 = 0; - - _ykpiv_begin_transaction(state)?; - - // send the response to the card and a challenge of our own. - let mut apdu = APDU::default(); - apdu.ins = YKPIV_INS_AUTHENTICATE; - apdu.p1 = YKPIV_ALGO_3DES; // triple des - apdu.p2 = YKPIV_KEY_CARDMGM; // management key - apdu.data[0] = 0x7c; - apdu.data[1] = 0x0a; // 2 + 8 - apdu.data[2] = 0x82; - apdu.data[3] = 8; - apdu.data[4..12].copy_from_slice(&response); - apdu.lc = 12; - - let mut res = _send_data(state, &mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw); - - if res.is_ok() && sw != SW_SUCCESS { - res = Err(Error::AuthenticationError); - } - - apdu.zeroize(); - let _ = _ykpiv_end_transaction(state); - res -} - -/// MGMT Application ID(?) -static mut MGMT_AID: [u8; 8] = [0xa0, 0x00, 0x00, 0x05, 0x27, 0x47, 0x11, 0x17]; - -/// Deauthenticate -pub unsafe fn ykpiv_auth_deauthenticate(state: &mut YubiKey) -> Result<(), Error> { - let mut data = [0u8; 255]; - let mut recv_len = data.len() as u32; - let mut sw: i32 = 0; - - _ykpiv_begin_transaction(state)?; - - let mut apdu = APDU::default(); - apdu.ins = YKPIV_INS_SELECT_APPLICATION; - apdu.p1 = 0x04; - apdu.lc = mem::size_of::<*const u8>() as u8; - - memcpy( - apdu.data.as_mut_ptr() as *mut c_void, - MGMT_AID.as_ptr() as *const c_void, - MGMT_AID.len(), - ); - - let mut res = _send_data(state, &mut apdu, data.as_mut_ptr(), &mut recv_len, &mut sw); - - if let Err(e) = &res { - error!("failed communicating with card: \'{}\'", e); - } - - if sw != SW_SUCCESS { - error!("Failed selecting mgmt application: {:04x}", sw); - res = Err(Error::GenericError); - } - - let _ = _ykpiv_end_transaction(state); - res -}