//! Utility functions // Adapted from yubico-piv-tool: // // // Copyright (c) 2014-2016 Yubico AB // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions are // met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above // copyright notice, this list of conditions and the following // disclaimer in the documentation and/or other materials provided // with the distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #![allow(non_camel_case_types, non_snake_case)] #![allow(clippy::missing_safety_doc, clippy::too_many_arguments)] use crate::{consts::*, error::ErrorKind, internal::*, yubikey::*}; use libc::{c_char, calloc, free, memcpy, memmove, realloc, time}; use std::{ffi::CString, mem, os::raw::c_void, ptr}; use zeroize::Zeroize; /// Cardholder Unique Identifier (CHUID) Template /// /// Format defined in SP-800-73-4, Appendix A, Table 9 /// /// FASC-N containing S9999F9999F999999F0F1F0000000000300001E encoded in /// 4-bit BCD with 1 bit parity. run through the tools/fasc.pl script to get /// bytes. This CHUID has an expiry of 2030-01-01. /// /// Defined fields: /// /// - 0x30: FASC-N (hard-coded) /// - 0x34: Card UUID / GUID (settable) /// - 0x35: Exp. Date (hard-coded) /// - 0x3e: Signature (hard-coded, empty) /// - 0xfe: Error Detection Code (hard-coded) pub const CHUID_TMPL: &[u8] = &[ 0x30, 0x19, 0xd4, 0xe7, 0x39, 0xda, 0x73, 0x9c, 0xed, 0x39, 0xce, 0x73, 0x9d, 0x83, 0x68, 0x58, 0x21, 0x08, 0x42, 0x10, 0x84, 0x21, 0xc8, 0x42, 0x10, 0xc3, 0xeb, 0x34, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x35, 0x08, 0x32, 0x30, 0x33, 0x30, 0x30, 0x31, 0x30, 0x31, 0x3e, 0x00, 0xfe, 0x00, ]; /// Cardholder Capability Container (CCC) Template /// /// f0: Card Identifier /// /// - 0xa000000116 == GSC-IS RID /// - 0xff == Manufacturer ID (dummy) /// - 0x02 == Card type (javaCard) /// - next 14 bytes: card ID pub static mut CCC_TMPL: &[u8] = &[ 0xf0, 0x15, 0xa0, 0x00, 0x00, 0x01, 0x16, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf1, 0x01, 0x21, 0xf2, 0x01, 0x21, 0xf3, 0x00, 0xf4, 0x01, 0x00, 0xf5, 0x01, 0x10, 0xf6, 0x00, 0xf7, 0x00, 0xfa, 0x00, 0xfb, 0x00, 0xfc, 0x00, 0xfd, 0x00, 0xfe, 0x00, ]; /// Card ID #[derive(Copy, Clone, Debug)] pub struct CardId([u8; 16]); /// Get Card ID pub unsafe fn ykpiv_util_get_cardid( state: &mut YubiKey, cardid: *mut CardId, ) -> Result<(), ErrorKind> { let mut buf = [0u8; CB_OBJ_MAX]; let mut len = buf.len(); let mut res: ErrorKind = ErrorKind::Ok; if cardid.is_null() { return Err(ErrorKind::GenericError); } if _ykpiv_begin_transaction(state) != ErrorKind::Ok { return Err(ErrorKind::PcscError); } if _ykpiv_ensure_application_selected(state) == ErrorKind::Ok { res = _ykpiv_fetch_object(state, YKPIV_OBJ_CHUID as i32, buf.as_mut_ptr(), &mut len); if res == ErrorKind::Ok { if len != CHUID_TMPL.len() { res = ErrorKind::GenericError; } else { memcpy( (*cardid).0.as_mut_ptr() as (*mut c_void), buf.as_mut_ptr().add(CHUID_GUID_OFFS) as (*const c_void), YKPIV_CARDID_SIZE, ); } } } _ykpiv_end_transaction(state); match res { ErrorKind::Ok => Ok(()), e => Err(e), } } /// Set Card ID pub unsafe fn ykpiv_util_set_cardid( state: &mut YubiKey, cardid: *const CardId, ) -> Result<(), ErrorKind> { let mut id = [0u8; YKPIV_CARDID_SIZE]; let mut buf = [0u8; CHUID_TMPL.len()]; let mut res = ErrorKind::Ok; if cardid.is_null() { if _ykpiv_prng_generate(id.as_mut_ptr(), id.len()) != PRngErrorKind::Ok { return Err(ErrorKind::RandomnessError); } } else { memcpy( id.as_mut_ptr() as (*mut c_void), (*cardid).0.as_ptr() as (*const c_void), id.len(), ); } if _ykpiv_begin_transaction(state) != ErrorKind::Ok { return Err(ErrorKind::PcscError); } if _ykpiv_ensure_application_selected(state) == ErrorKind::Ok { memcpy( buf.as_mut_ptr() as *mut c_void, CHUID_TMPL.as_ptr() as *const c_void, buf.len(), ); memcpy( buf.as_mut_ptr().add(CHUID_GUID_OFFS) as *mut c_void, id.as_mut_ptr() as *const c_void, id.len(), ); res = _ykpiv_save_object( state, YKPIV_OBJ_CHUID as i32, buf.as_mut_ptr(), CHUID_TMPL.len(), ); } _ykpiv_end_transaction(state); match res { ErrorKind::Ok => Ok(()), e => Err(e), } } /// Cardholder Capability Container (CCC) Identifier #[derive(Copy, Clone, Debug, Eq, PartialEq)] 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<(), ErrorKind> { let mut res: ErrorKind = ErrorKind::Ok; let mut buf = [0u8; CB_OBJ_MAX]; let mut len = buf.len(); if ccc.is_null() { return Err(ErrorKind::GenericError); } if _ykpiv_begin_transaction(state) != ErrorKind::Ok { return Err(ErrorKind::PcscError); } if _ykpiv_ensure_application_selected(state) == ErrorKind::Ok { res = _ykpiv_fetch_object( state, YKPIV_OBJ_CAPABILITY as i32, buf.as_mut_ptr(), &mut len, ); if res == ErrorKind::Ok { if len != CCC_TMPL.len() { _ykpiv_end_transaction(state); return Err(ErrorKind::GenericError); } memcpy( (*ccc).0.as_mut_ptr() as (*mut c_void), buf.as_mut_ptr().offset(9) as (*const c_void), YKPIV_CCCID_SIZE, ); } } match res { ErrorKind::Ok => Ok(()), e => Err(e), } } /// Get Cardholder Capability Container (CCC) ID pub unsafe fn ykpiv_util_set_cccid( state: &mut YubiKey, ccc: *const CCCID, ) -> Result<(), ErrorKind> { let mut res: ErrorKind = ErrorKind::Ok; let mut id = [0u8; 14]; let mut buf = [0u8; 51]; let len: usize; if ccc.is_null() { if _ykpiv_prng_generate(id.as_mut_ptr(), id.len()) != PRngErrorKind::Ok { return Err(ErrorKind::RandomnessError); } } else { memcpy( id.as_mut_ptr() as (*mut c_void), (*ccc).0.as_ptr() as (*const c_void), id.len(), ); } if _ykpiv_begin_transaction(state) != ErrorKind::Ok { return Err(ErrorKind::PcscError); } if _ykpiv_ensure_application_selected(state) == ErrorKind::Ok { len = 51; memcpy( buf.as_mut_ptr() as *mut c_void, CCC_TMPL.as_ptr() as *const c_void, len, ); memcpy( buf.as_mut_ptr().offset(9) as (*mut c_void), id.as_mut_ptr() as (*const c_void), 14, ); res = _ykpiv_save_object(state, YKPIV_OBJ_CAPABILITY as i32, buf.as_mut_ptr(), len); } _ykpiv_end_transaction(state); match res { ErrorKind::Ok => Ok(()), e => Err(e), } } /// Get YubiKey device model pub unsafe fn ykpiv_util_devicemodel(state: &mut YubiKey) -> u32 { if state.context == 0 || state.context == -1 { DEVTYPE_UNKNOWN } else if state.is_neo { DEVTYPE_NEOr3 } else { DEVTYPE_YK4 } } /// Personal Identity Verification (PIV) keys #[derive(Copy, Clone, Debug)] pub struct YkPivKey { /// Card slot slot: u8, /// Length of cert cert_len: u16, /// Cert cert: [u8; 1], } /// Personal Identity Verification (PIV) key slots pub const SLOTS: [u8; 24] = [ YKPIV_KEY_AUTHENTICATION, YKPIV_KEY_SIGNATURE, YKPIV_KEY_KEYMGM, YKPIV_KEY_RETIRED1, YKPIV_KEY_RETIRED2, YKPIV_KEY_RETIRED3, YKPIV_KEY_RETIRED4, YKPIV_KEY_RETIRED5, YKPIV_KEY_RETIRED6, YKPIV_KEY_RETIRED7, YKPIV_KEY_RETIRED8, YKPIV_KEY_RETIRED9, YKPIV_KEY_RETIRED10, YKPIV_KEY_RETIRED11, YKPIV_KEY_RETIRED12, YKPIV_KEY_RETIRED13, YKPIV_KEY_RETIRED14, YKPIV_KEY_RETIRED15, YKPIV_KEY_RETIRED16, YKPIV_KEY_RETIRED17, YKPIV_KEY_RETIRED18, YKPIV_KEY_RETIRED19, YKPIV_KEY_RETIRED20, YKPIV_KEY_CARDAUTH, ]; /// List Personal Identity Verification (PIV) keys // TODO(tarcieri): fix clippy alignment warnings #[allow(clippy::cast_ptr_alignment)] pub unsafe fn ykpiv_util_list_keys( state: &mut YubiKey, key_count: *mut u8, data: *mut *mut YkPivKey, data_len: *mut usize, ) -> Result<(), ErrorKind> { let mut _currentBlock; let mut res: ErrorKind = ErrorKind::Ok; let mut p_key: *mut YkPivKey; let mut p_data: *mut u8 = ptr::null_mut(); let mut p_temp: *mut u8; let mut cb_data: usize; let mut offset: usize = 0; let mut buf = [0u8; YKPIV_OBJ_MAX_SIZE]; let mut cb_buf: usize; let mut i: usize; let mut cb_realloc: usize; let CB_PAGE: usize = 4096; if data.is_null() || data_len.is_null() || key_count.is_null() { return Err(ErrorKind::GenericError); } if _ykpiv_begin_transaction(state) != ErrorKind::Ok { return Err(ErrorKind::PcscError); } if _ykpiv_ensure_application_selected(state) == ErrorKind::Ok { *key_count = 0; *data = ptr::null_mut(); *data_len = 0; p_data = calloc(CB_PAGE, 1) as (*mut u8); if p_data.is_null() { _ykpiv_end_transaction(state); return Err(ErrorKind::MemoryError); } cb_data = CB_PAGE; i = 0; loop { if i >= mem::size_of::<*const u8>() { _currentBlock = 6; break; } cb_buf = buf.len(); res = _read_certificate(state, SLOTS[i], buf.as_mut_ptr(), &mut cb_buf); if res == ErrorKind::Ok && (cb_buf > 0) { cb_realloc = if mem::size_of::() .wrapping_add(cb_buf) .wrapping_sub(1) > cb_data.wrapping_sub(offset) { (if mem::size_of::() .wrapping_add(cb_buf) .wrapping_sub(1) .wrapping_sub(cb_data.wrapping_sub(offset)) > CB_PAGE { mem::size_of::() .wrapping_add(cb_buf) .wrapping_sub(1) .wrapping_sub(cb_data.wrapping_sub(offset)) } else { CB_PAGE }) } else { 0 }; if cb_realloc != 0 { if { p_temp = realloc(p_data as (*mut c_void), cb_data.wrapping_add(cb_realloc)) as (*mut u8); p_temp } .is_null() { _currentBlock = 15; break; } p_data = p_temp; } cb_data = cb_data.wrapping_add(cb_realloc); p_key = p_data.add(offset) as *mut YkPivKey; (*p_key).slot = SLOTS[i]; (*p_key).cert_len = cb_buf as (u16); memcpy( (*p_key).cert.as_mut_ptr() as *mut c_void, buf.as_mut_ptr() as *const c_void, cb_buf, ); offset = offset.wrapping_add( mem::size_of::() .wrapping_add(cb_buf) .wrapping_sub(1), ); *key_count = (*key_count as i32 + 1) as u8; } i += 1; } if _currentBlock == 6 { *data = p_data as *mut YkPivKey; p_data = ptr::null_mut(); if !data_len.is_null() { *data_len = offset; } res = ErrorKind::Ok; } else { res = ErrorKind::MemoryError; } } if !p_data.is_null() { free(p_data as (*mut c_void)); } _ykpiv_end_transaction(state); match res { ErrorKind::Ok => Ok(()), e => Err(e), } } /// Read certificate pub unsafe fn ykpiv_util_read_cert( state: &mut YubiKey, slot: u8, data: *mut *mut u8, data_len: *mut usize, ) -> Result<(), ErrorKind> { let mut res: ErrorKind = ErrorKind::Ok; let mut buf = [0u8; YKPIV_OBJ_MAX_SIZE]; let mut cb_buf: usize = buf.len(); if data.is_null() || data_len.is_null() { return Err(ErrorKind::GenericError); } if _ykpiv_begin_transaction(state) != ErrorKind::Ok { return Err(ErrorKind::PcscError); } if _ykpiv_ensure_application_selected(state) == ErrorKind::Ok { *data = ptr::null_mut(); *data_len = 0; res = _read_certificate(state, slot, buf.as_mut_ptr(), &mut cb_buf); if res == ErrorKind::Ok { if cb_buf == 0 { *data = ptr::null_mut(); *data_len = 0; } else if { *data = calloc(cb_buf, 1) as *mut u8; *data } .is_null() { res = ErrorKind::MemoryError; } else { memcpy( *data as (*mut c_void), buf.as_mut_ptr() as (*const c_void), cb_buf, ); *data_len = cb_buf; } } } _ykpiv_end_transaction(state); match res { ErrorKind::Ok => Ok(()), e => Err(e), } } /// Write certificate pub unsafe fn ykpiv_util_write_cert( state: &mut YubiKey, slot: u8, data: *mut u8, data_len: usize, certinfo: u8, ) -> Result<(), ErrorKind> { let mut res: ErrorKind = ErrorKind::Ok; if _ykpiv_begin_transaction(state) != ErrorKind::Ok { return Err(ErrorKind::PcscError); } if _ykpiv_ensure_application_selected(state) == ErrorKind::Ok { res = _write_certificate(state, slot, data, data_len, certinfo); } _ykpiv_end_transaction(state); match res { ErrorKind::Ok => Ok(()), e => Err(e), } } /// Delete certificate pub unsafe fn ykpiv_util_delete_cert(state: &mut YubiKey, slot: u8) -> Result<(), ErrorKind> { ykpiv_util_write_cert(state, slot, ptr::null_mut(), 0, 0) } /// Block PUK pub unsafe fn ykpiv_util_block_puk(state: &mut YubiKey) -> Result<(), ErrorKind> { let mut _currentBlock; let mut res: ErrorKind = ErrorKind::Ok; let mut puk = [0x30, 0x42, 0x41, 0x44, 0x46, 0x30, 0x30, 0x44]; let mut tries: i32 = -1; let mut data = [0u8; YKPIV_OBJ_MAX_SIZE]; let mut cb_data: usize = data.len(); let mut p_item: *mut u8 = ptr::null_mut(); let mut cb_item: usize = 0; let mut flags: u8 = 0; if _ykpiv_begin_transaction(state) != ErrorKind::Ok { return Err(ErrorKind::PcscError); } if _ykpiv_ensure_application_selected(state) == ErrorKind::Ok { _currentBlock = 20; } else { _currentBlock = 3; } loop { if _currentBlock == 3 { if tries != 0 { res = ykpiv_change_puk( state, puk.as_ptr(), mem::size_of::<*const c_char>(), puk.as_ptr(), mem::size_of::<*const c_char>(), &mut tries, ); if res == ErrorKind::Ok { let _rhs = 1; let mut _lhs = &mut puk[0]; *_lhs += _rhs; _currentBlock = 3; } else { if res != ErrorKind::PinLocked { _currentBlock = 3; continue; } tries = 0; res = ErrorKind::Ok; _currentBlock = 3; } } else { let res = _read_metadata(state, TAG_ADMIN, data.as_mut_ptr(), &mut cb_data); if res == ErrorKind::Ok { let res = _get_metadata_item( data.as_mut_ptr(), cb_data, TAG_ADMIN_FLAGS_1, &mut p_item, &mut cb_item, ); if res == ErrorKind::Ok { if cb_item == 1 { // TODO(tarcieri): get rid of memcpy and pointers, replace with slices! #[allow(trivial_casts)] memcpy( &mut flags as *mut u8 as *mut c_void, p_item as (*const c_void), cb_item, ); } else if state.verbose != 0 { eprintln!("admin flags exist, but are incorrect size = {}", cb_item,); } } } flags |= 0x1; if _set_metadata_item( data.as_mut_ptr(), &mut cb_data, CB_OBJ_MAX, TAG_ADMIN_FLAGS_1, &mut flags, 1, ) != ErrorKind::Ok { if state.verbose == 0 { _currentBlock = 20; continue; } eprintln!("could not set admin flags"); _currentBlock = 20; } else { if _write_metadata(state, 0x80u8, data.as_mut_ptr(), cb_data) == ErrorKind::Ok { _currentBlock = 20; continue; } if state.verbose == 0 { _currentBlock = 20; continue; } eprintln!("could not write admin metadata"); _currentBlock = 20; } } } else { _ykpiv_end_transaction(state); return match res { ErrorKind::Ok => Ok(()), e => Err(e), }; } } } /// PIV container // TODO(tarcieri): dead code? #[allow(dead_code)] #[derive(Copy, Clone)] pub struct YkPivContainer { /// Name name: [i32; 40], /// Card slot slot: u8, /// Key spec key_spec: u8, /// Key size in bits key_size_bits: u16, /// Flags flags: u8, /// PIN ID pin_id: u8, /// Associated ECHD container associated_echd_container: u8, /// Cert fingerprint cert_fingerprint: [u8; 20], } /// Read mscmap pub unsafe fn ykpiv_util_read_mscmap( state: &mut YubiKey, containers: *mut *mut YkPivContainer, n_containers: *mut usize, ) -> Result<(), ErrorKind> { let mut res: ErrorKind = ErrorKind::Ok; let mut buf = [0u8; YKPIV_OBJ_MAX_SIZE]; let mut cb_buf: usize = buf.len(); let mut len: usize = 0; let mut ptr: *mut u8; if containers.is_null() || n_containers.is_null() { // TODO(str4d): Should this really continue on here? res = ErrorKind::GenericError; } if _ykpiv_begin_transaction(state) != ErrorKind::Ok { return Err(ErrorKind::PcscError); } if _ykpiv_ensure_application_selected(state) == ErrorKind::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, ); if res != ErrorKind::Ok { _ykpiv_end_transaction(state); return match res { ErrorKind::Ok => Ok(()), e => Err(e), }; } ptr = buf.as_mut_ptr(); if cb_buf < CB_OBJ_TAG_MIN { _ykpiv_end_transaction(state); return Ok(()); } if *ptr == TAG_MSCMAP { ptr = ptr.add(1); ptr = ptr.add(_ykpiv_get_length(ptr, &mut len)); if len > cb_buf - (ptr as isize - buf.as_mut_ptr() as isize) as usize { _ykpiv_end_transaction(state); return Ok(()); } *containers = calloc(len, 1) as (*mut YkPivContainer); if (*containers).is_null() { res = ErrorKind::MemoryError; } else { memcpy(*containers as (*mut c_void), ptr as (*const c_void), len); *n_containers = len.wrapping_div(mem::size_of::()); } } } match res { ErrorKind::Ok => Ok(()), e => Err(e), } } /// Get max object size unsafe fn _obj_size_max(state: &mut YubiKey) -> usize { if state.is_neo { 2048 - 9 } else { CB_OBJ_MAX } } /// Write mscmap pub unsafe fn ykpiv_util_write_mscmap( state: &mut YubiKey, containers: *mut YkPivContainer, n_containers: usize, ) -> Result<(), ErrorKind> { let mut res = ErrorKind::Ok; let mut buf = [0u8; CB_OBJ_MAX]; let mut offset: usize = 0; let data_len: usize = n_containers.wrapping_mul(mem::size_of::()); if _ykpiv_begin_transaction(state) != ErrorKind::Ok { return Err(ErrorKind::PcscError); } if _ykpiv_ensure_application_selected(state) == ErrorKind::Ok { if containers.is_null() || n_containers == 0 { if !containers.is_null() || n_containers != 0 { res = ErrorKind::GenericError; } else { res = _ykpiv_save_object(state, YKPIV_OBJ_MSCMAP as i32, ptr::null_mut(), 0); } _ykpiv_end_transaction(state); return match res { ErrorKind::Ok => Ok(()), e => Err(e), }; } let req_len = 1 + _ykpiv_set_length(buf.as_mut_ptr(), data_len) + data_len; if req_len > _obj_size_max(state) { _ykpiv_end_transaction(state); return Err(ErrorKind::SizeError); } buf[offset] = 0x81; offset += 1; offset += _ykpiv_set_length(buf.as_mut_ptr().add(offset), data_len); memcpy( buf.as_mut_ptr().add(offset) as (*mut c_void), containers as (*mut u8) as (*const c_void), data_len, ); offset = offset.wrapping_add(data_len); res = _ykpiv_save_object(state, YKPIV_OBJ_MSCMAP as i32, buf.as_mut_ptr(), offset); } _ykpiv_end_transaction(state); match res { ErrorKind::Ok => Ok(()), e => Err(e), } } /// Read msroots pub unsafe fn ykpiv_util_read_msroots( state: &mut YubiKey, data: *mut *mut u8, data_len: *mut usize, ) -> Result<(), ErrorKind> { let mut _currentBlock; let mut res = ErrorKind::Ok; let mut buf = [0u8; YKPIV_OBJ_MAX_SIZE]; let mut cb_buf: usize; let mut len: usize = 0; let mut ptr: *mut u8; let mut object_id: i32; let mut tag: u8; let mut p_data: *mut u8 = ptr::null_mut(); let mut p_temp: *mut u8; let mut cb_data: usize; let mut cb_realloc: usize; let mut offset: usize = 0; if data.is_null() || data_len.is_null() { return Err(ErrorKind::GenericError); } if _ykpiv_begin_transaction(state) != ErrorKind::Ok { return Err(ErrorKind::PcscError); } if _ykpiv_ensure_application_selected(state) == ErrorKind::Ok { *data = ptr::null_mut(); *data_len = 0; cb_data = _obj_size_max(state); p_data = calloc(cb_data, 1) as (*mut u8); if p_data.is_null() { res = ErrorKind::MemoryError; } else { object_id = YKPIV_OBJ_MSROOTS1 as i32; loop { if object_id > YKPIV_OBJ_MSROOTS5 as i32 { _currentBlock = 15; break; } cb_buf = buf.len(); res = _ykpiv_fetch_object(state, object_id, buf.as_mut_ptr(), &mut cb_buf); if res != ErrorKind::Ok { _currentBlock = 21; break; } ptr = buf.as_mut_ptr(); if cb_buf < 2 { _currentBlock = 19; break; } tag = *{ let _old = ptr; ptr = ptr.offset(1); _old }; if tag != 0x82 && (tag != 0x83 || object_id == YKPIV_OBJ_MSROOTS5 as i32) { _currentBlock = 18; break; } ptr = ptr.add(_ykpiv_get_length(ptr, &mut len)); if len > cb_buf - (ptr as isize - buf.as_mut_ptr() as isize) as usize { _currentBlock = 17; break; } cb_realloc = if len > cb_data.wrapping_sub(offset) { len.wrapping_sub(cb_data.wrapping_sub(offset)) } else { 0 }; if cb_realloc != 0 { if { p_temp = realloc(p_data as (*mut c_void), cb_data.wrapping_add(cb_realloc)) as (*mut u8); p_temp } .is_null() { _currentBlock = 16; break; } p_data = p_temp; } cb_data = cb_data.wrapping_add(cb_realloc); memcpy( p_data.add(offset) as (*mut c_void), ptr as (*const c_void), len, ); offset = offset.wrapping_add(len); if tag == 0x82 { _currentBlock = 15; break; } object_id += 1; } if _currentBlock == 21 { } else if _currentBlock == 15 { *data = p_data; p_data = ptr::null_mut(); *data_len = offset; res = ErrorKind::Ok; } else if _currentBlock == 16 { res = ErrorKind::MemoryError; } else { res = ErrorKind::Ok; } } } if !p_data.is_null() { free(p_data as (*mut c_void)); } _ykpiv_end_transaction(state); match res { ErrorKind::Ok => Ok(()), e => Err(e), } } /// Write msroots pub unsafe fn ykpiv_util_write_msroots( state: &mut YubiKey, data: *mut u8, data_len: usize, ) -> Result<(), ErrorKind> { let mut res: ErrorKind = ErrorKind::Ok; let mut buf = [0u8; CB_OBJ_MAX]; let mut offset: usize; let mut data_offset: usize = 0; let mut data_chunk: usize; let n_objs: usize; let cb_obj_max = _obj_size_max(state); if _ykpiv_begin_transaction(state) != ErrorKind::Ok { return Err(ErrorKind::PcscError); } if _ykpiv_ensure_application_selected(state) == ErrorKind::Ok { if data.is_null() || data_len == 0 { if !data.is_null() || data_len != 0 { res = ErrorKind::GenericError; } else { res = _ykpiv_save_object(state, YKPIV_OBJ_MSROOTS1 as i32, ptr::null_mut(), 0); } _ykpiv_end_transaction(state); return match res { ErrorKind::Ok => Ok(()), e => Err(e), }; } n_objs = (data_len / (cb_obj_max - 4)) + 1; if n_objs > 5 { _ykpiv_end_transaction(state); return Err(ErrorKind::SizeError); } for i in 0..n_objs { offset = 0; data_chunk = if cb_obj_max - 4 < data_len - data_offset { cb_obj_max - 4 } else { data_len - data_offset }; buf[offset] = if i == n_objs - 1 { TAG_MSROOTS_END } else { TAG_MSROOTS_MID }; offset += 1; offset += _ykpiv_set_length(buf.as_mut_ptr().add(offset), data_chunk); memcpy( buf.as_mut_ptr().add(offset) as *mut c_void, data.add(data_offset) as *const c_void, data_chunk, ); offset = offset.wrapping_add(data_chunk); res = _ykpiv_save_object( state, (YKPIV_OBJ_MSROOTS1 + i as u32) as i32, buf.as_mut_ptr(), offset, ); if res != ErrorKind::Ok { break; } data_offset = data_offset.wrapping_add(data_chunk); } } _ykpiv_end_transaction(state); match res { ErrorKind::Ok => Ok(()), e => Err(e), } } // Keygen messages // TODO(tarcieri): extract these into an I18N-handling type? const SZ_SETTING_ROCA: &str = "Enable_Unsafe_Keygen_ROCA"; const SZ_ROCA_ALLOW_USER: &str = "was permitted by an end-user configuration setting, but is not recommended."; const SZ_ROCA_ALLOW_ADMIN: &str = "was permitted by an administrator configuration setting, but is not recommended."; const SZ_ROCA_BLOCK_USER: &str = "was blocked due to an end-user configuration setting."; const SZ_ROCA_BLOCK_ADMIN: &str = "was blocked due to an administrator configuration setting."; const SZ_ROCA_DEFAULT: &str = "was permitted by default, but is not recommended. The default behavior will change in a future Yubico release."; /// Generate key #[allow(clippy::cognitive_complexity)] pub unsafe fn ykpiv_util_generate_key( state: &mut YubiKey, slot: u8, algorithm: u8, pin_policy: u8, touch_policy: u8, modulus: *mut *mut u8, modulus_len: *mut usize, exp: *mut *mut u8, exp_len: *mut usize, point: *mut *mut u8, point_len: *mut usize, ) -> Result<(), ErrorKind> { let mut res: ErrorKind = ErrorKind::Ok; let mut in_data = [0u8; 11]; let mut in_ptr = in_data.as_mut_ptr(); let mut data = [0u8; 1024]; let mut templ = [0, YKPIV_INS_GENERATE_ASYMMETRIC, 0, 0]; let mut recv_len = data.len(); let mut sw: i32 = 0; let mut ptr_modulus: *mut u8 = ptr::null_mut(); let cb_modulus: usize; let mut ptr_exp: *mut u8 = ptr::null_mut(); let cb_exp: usize; let mut ptr_point: *mut u8 = ptr::null_mut(); let cb_point: usize; let setting_roca: SettingBool; if ykpiv_util_devicemodel(state) == 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)) { let setting_name = CString::new(SZ_SETTING_ROCA).unwrap(); setting_roca = setting_get_bool(setting_name.as_ptr(), true); let psz_msg = match setting_roca.source { SettingSource::User => { if setting_roca.value { SZ_ROCA_ALLOW_USER } else { SZ_ROCA_BLOCK_USER } } SettingSource::Admin => { if setting_roca.value { SZ_ROCA_ALLOW_ADMIN } else { SZ_ROCA_BLOCK_ADMIN } } _ => SZ_ROCA_DEFAULT, }; eprintln!( "YubiKey serial number {} is affected by vulnerability CVE-2017-15361 \ (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 ); if !setting_roca.value { return Err(ErrorKind::NotSupported); } } match algorithm { YKPIV_ALGO_RSA1024 | YKPIV_ALGO_RSA2048 => { if point.is_null() || point_len.is_null() { if state.verbose != 0 { eprintln!("Invalid output parameter for ECC algorithm"); } return Err(ErrorKind::GenericError); } else { *point = ptr::null_mut(); *point_len = 0; } } YKPIV_ALGO_ECCP256 | YKPIV_ALGO_ECCP384 => { if modulus.is_null() || modulus_len.is_null() || exp.is_null() || exp_len.is_null() { if state.verbose != 0 { eprintln!("Invalid output parameter for RSA algorithm",); } return Err(ErrorKind::GenericError); } else { *modulus = ptr::null_mut(); *modulus_len = 0; *exp = ptr::null_mut(); *exp_len = 0; } } _ => { if state.verbose != 0 { eprintln!("Invalid algorithm specified"); } return Err(ErrorKind::GenericError); } } if _ykpiv_begin_transaction(state) != ErrorKind::Ok { return Err(ErrorKind::PcscError); } if _ykpiv_ensure_application_selected(state) == ErrorKind::Ok { templ[3] = slot; *{ let _old = in_ptr; in_ptr = in_ptr.offset(1); _old } = 0xac; *{ let _old = in_ptr; in_ptr = in_ptr.offset(1); _old } = 3; *{ let _old = in_ptr; in_ptr = in_ptr.offset(1); _old } = YKPIV_ALGO_TAG; *{ let _old = in_ptr; in_ptr = in_ptr.offset(1); _old } = 1; *{ let _old = in_ptr; in_ptr = in_ptr.offset(1); _old } = algorithm; if in_data[4] == 0 { res = ErrorKind::AlgorithmError; if state.verbose != 0 { eprintln!("Unexpected algorithm.\n"); } } else { if pin_policy != YKPIV_PINPOLICY_DEFAULT { let _rhs = 3; let _lhs = &mut in_data[1]; *_lhs = (*_lhs as (i32) + _rhs) as (u8); *{ let _old = in_ptr; in_ptr = in_ptr.offset(1); _old } = YKPIV_PINPOLICY_TAG; *{ let _old = in_ptr; in_ptr = in_ptr.offset(1); _old } = 1u8; *{ let _old = in_ptr; in_ptr = in_ptr.offset(1); _old } = pin_policy; } if touch_policy != YKPIV_TOUCHPOLICY_DEFAULT { let _rhs = 3i32; let _lhs = &mut in_data[1]; *_lhs = (*_lhs as (i32) + _rhs) as (u8); *{ let _old = in_ptr; in_ptr = in_ptr.offset(1); _old } = YKPIV_TOUCHPOLICY_TAG; *{ let _old = in_ptr; in_ptr = in_ptr.offset(1); _old } = 1u8; *{ let _old = in_ptr; in_ptr = in_ptr.offset(1); _old } = touch_policy; } res = _ykpiv_transfer_data( state, templ.as_ptr(), in_data.as_mut_ptr(), in_ptr as isize - in_data.as_mut_ptr() as isize, data.as_mut_ptr(), &mut recv_len, &mut sw, ); if res != ErrorKind::Ok { if state.verbose != 0 { eprintln!("Failed to communicate."); } } else if sw != SW_SUCCESS { if state.verbose != 0 { eprint!("Failed to generate new key ("); } match sw { SW_ERR_INCORRECT_SLOT => { res = ErrorKind::KeyError; if state.verbose != 0 { eprintln!("incorrect slot)"); } } SW_ERR_INCORRECT_PARAM => { res = ErrorKind::AlgorithmError; if state.verbose != 0 { if pin_policy as (i32) != 0i32 { eprintln!("pin policy not supported?)",); } else if touch_policy as (i32) != 0i32 { eprintln!("touch policy not supported?)",); } else { eprintln!("algorithm not supported?)",); } } } SW_ERR_SECURITY_STATUS => { res = ErrorKind::AuthenticationError; if state.verbose != 0 { eprintln!("not authenticated)"); } } _ => { res = ErrorKind::GenericError; if state.verbose != 0 { eprintln!("error {:x})", sw); } } } } else if algorithm == YKPIV_ALGO_RSA1024 || algorithm == YKPIV_ALGO_RSA2048 { let mut data_ptr: *mut u8 = data.as_mut_ptr().offset(5); let mut len: usize = 0; if *data_ptr != TAG_RSA_MODULUS { if state.verbose != 0 { eprintln!("Failed to parse public key structure (modulus)."); } res = ErrorKind::ParseError; } else { data_ptr = data_ptr.add(1); data_ptr = data_ptr.add(_ykpiv_get_length(data_ptr, &mut len)); cb_modulus = len; ptr_modulus = calloc(cb_modulus, 1) as *mut u8; if ptr_modulus.is_null() { if state.verbose != 0 { eprintln!("Failed to allocate memory for modulus."); } res = ErrorKind::MemoryError; } else { memcpy( ptr_modulus as *mut c_void, data_ptr as *const c_void, cb_modulus, ); data_ptr = data_ptr.add(len); if *data_ptr != TAG_RSA_EXP { if state.verbose != 0 { eprintln!( "Failed to parse public key structure (public exponent)." ); } res = ErrorKind::ParseError; } else { data_ptr = data_ptr.add(1); data_ptr = data_ptr.add(_ykpiv_get_length(data_ptr, &mut len)); cb_exp = len; ptr_exp = calloc(cb_exp, 1) as *mut u8; if ptr_exp.is_null() { if state.verbose != 0 { eprintln!("Failed to allocate memory for public exponent."); } res = ErrorKind::MemoryError; } else { memcpy( ptr_exp as (*mut c_void), data_ptr as (*const c_void), cb_exp, ); *modulus = ptr_modulus; ptr_modulus = ptr::null_mut(); *modulus_len = cb_modulus; *exp = ptr_exp; ptr_exp = ptr::null_mut(); *exp_len = cb_exp; } } } } } else if algorithm == YKPIV_ALGO_ECCP256 || algorithm == YKPIV_ALGO_ECCP384 { let mut data_ptr: *mut u8 = data.as_mut_ptr().offset(3); let len = if algorithm == YKPIV_ALGO_ECCP256 { CB_ECC_POINTP256 } else { CB_ECC_POINTP384 }; if *{ let _old = data_ptr; data_ptr = data_ptr.offset(1); _old } != TAG_ECC_POINT { if state.verbose != 0 { eprintln!("Failed to parse public key structure.\n",); } res = ErrorKind::ParseError; } else if *{ let _old = data_ptr; data_ptr = data_ptr.offset(1); _old } as (usize) != len { if state.verbose != 0 { eprintln!("Unexpected length.\n"); } res = ErrorKind::AlgorithmError; } else { cb_point = len; ptr_point = calloc(cb_point, 1) as (*mut u8); if ptr_point.is_null() { if state.verbose != 0 { eprintln!("Failed to allocate memory for public point."); } res = ErrorKind::MemoryError; } else { memcpy( ptr_point as (*mut c_void), data_ptr as (*const c_void), cb_point, ); *point = ptr_point; ptr_point = ptr::null_mut(); *point_len = cb_point; } } } else { if state.verbose != 0 { eprintln!("Wrong algorithm."); } res = ErrorKind::AlgorithmError; } } } if !ptr_modulus.is_null() { free(modulus as (*mut c_void)); } if !ptr_exp.is_null() { free(ptr_exp as (*mut c_void)); } if !ptr_point.is_null() { free(ptr_exp as (*mut c_void)); } _ykpiv_end_transaction(state); match res { ErrorKind::Ok => Ok(()), e => Err(e), } } /// Config mgm type #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(i32)] #[allow(non_camel_case_types)] pub enum YkPivConfigMgmType { /// Manual YKPIV_CONFIG_MGM_MANUAL = 0i32, /// Derived YKPIV_CONFIG_MGM_DERIVED = 1i32, /// Protected YKPIV_CONFIG_MGM_PROTECTED = 2i32, } /// Config #[derive(Copy, Clone)] pub struct YkPivConfig { /// Protected data available protected_data_available: u8, /// PUK blocked puk_blocked: u8, /// No block on upgrade puk_noblock_on_upgrade: u8, /// PIN last changed pin_last_changed: u32, /// MGM type mgm_type: YkPivConfigMgmType, } /// Get config pub unsafe fn ykpiv_util_get_config( state: &mut YubiKey, config: *mut YkPivConfig, ) -> Result<(), ErrorKind> { let mut data = [0u8; YKPIV_OBJ_MAX_SIZE]; let mut cb_data: usize = mem::size_of::<[u8; YKPIV_OBJ_MAX_SIZE]>(); let mut p_item: *mut u8 = ptr::null_mut(); let mut cb_item: usize = 0; let res = ErrorKind::Ok; if config.is_null() { return Err(ErrorKind::GenericError); } (*config).protected_data_available = 0u8; (*config).puk_blocked = 0u8; (*config).puk_noblock_on_upgrade = 0u8; (*config).pin_last_changed = 0u32; (*config).mgm_type = YkPivConfigMgmType::YKPIV_CONFIG_MGM_MANUAL; if _ykpiv_begin_transaction(state) != ErrorKind::Ok { return Err(ErrorKind::PcscError); } if _ykpiv_ensure_application_selected(state) == ErrorKind::Ok { if _read_metadata(state, 0x80u8, data.as_mut_ptr(), &mut cb_data) == ErrorKind::Ok { if _get_metadata_item( data.as_mut_ptr(), cb_data, 0x81u8, &mut p_item, &mut cb_item, ) == ErrorKind::Ok { if *p_item & 0x1 != 0 { (*config).puk_blocked = 1u8; } if *p_item & 0x2 != 0 { (*config).mgm_type = YkPivConfigMgmType::YKPIV_CONFIG_MGM_PROTECTED; } } if _get_metadata_item( data.as_mut_ptr(), cb_data, 0x82u8, &mut p_item, &mut cb_item, ) == ErrorKind::Ok { if (*config).mgm_type as (i32) != YkPivConfigMgmType::YKPIV_CONFIG_MGM_MANUAL as (i32) { if state.verbose != 0 { eprintln!("conflicting types of mgm key administration configured"); } } else { (*config).mgm_type = YkPivConfigMgmType::YKPIV_CONFIG_MGM_DERIVED; } } if _get_metadata_item( data.as_mut_ptr(), cb_data, 0x83u8, &mut p_item, &mut cb_item, ) == ErrorKind::Ok { if cb_item != 4 { if state.verbose != 0 { eprintln!("pin timestamp in admin metadata is an invalid size"); } } else { // TODO(tarcieri): get rid of memcpy and pointers, replace with slices! #[allow(trivial_casts)] memcpy( &mut (*config).pin_last_changed as (*mut u32) as (*mut c_void), p_item as (*const c_void), cb_item, ); } } } cb_data = mem::size_of::<[u8; YKPIV_OBJ_MAX_SIZE]>(); if _read_metadata(state, 0x88u8, data.as_mut_ptr(), &mut cb_data) == ErrorKind::Ok { (*config).protected_data_available = 1u8; let res = _get_metadata_item( data.as_mut_ptr(), cb_data, 0x81u8, &mut p_item, &mut cb_item, ); if res == ErrorKind::Ok && *p_item as (i32) & 0x1i32 != 0 { (*config).puk_noblock_on_upgrade = 1u8; } let res = _get_metadata_item( data.as_mut_ptr(), cb_data, 0x89u8, &mut p_item, &mut cb_item, ); if res == ErrorKind::Ok { if (*config).mgm_type != YkPivConfigMgmType::YKPIV_CONFIG_MGM_PROTECTED && state.verbose != 0 { eprintln!( "conflicting types of mgm key administration configured - protected mgm exists" ); } (*config).mgm_type = YkPivConfigMgmType::YKPIV_CONFIG_MGM_PROTECTED; } } } _ykpiv_end_transaction(state); match res { ErrorKind::Ok => Ok(()), e => Err(e), } } /// Set PIN last changed pub unsafe fn ykpiv_util_set_pin_last_changed(state: &mut YubiKey) -> Result<(), ErrorKind> { let mut data = [0u8; YKPIV_OBJ_MAX_SIZE]; let mut cb_data = data.len(); let mut res = ErrorKind::Ok; let ykrc: ErrorKind; if _ykpiv_begin_transaction(state) != ErrorKind::Ok { return Err(ErrorKind::PcscError); } if _ykpiv_ensure_application_selected(state) == ErrorKind::Ok { ykrc = _read_metadata(state, 0x80, data.as_mut_ptr(), &mut cb_data); if ykrc != ErrorKind::Ok { cb_data = 0; } let mut tnow = time(ptr::null_mut()); res = { // TODO(tarcieri): get rid of memcpy and pointers, replace with slices! #[allow(trivial_casts)] _set_metadata_item( data.as_mut_ptr(), &mut cb_data, CB_OBJ_MAX, 0x83, &mut tnow as *mut i64 as *mut u8, 4, ) }; if res != ErrorKind::Ok { if state.verbose != 0 { eprintln!("could not set pin timestamp, err = {}\n", res as (i32),); } } else { res = _write_metadata(state, 0x80u8, data.as_mut_ptr(), cb_data); if res != ErrorKind::Ok && state.verbose != 0 { eprintln!("could not write admin data, err = {}", res); } } } _ykpiv_end_transaction(state); match res { ErrorKind::Ok => Ok(()), e => Err(e), } } /// Management key (MGM) #[derive(Clone)] pub struct YkPivMgm([u8; 24]); impl Zeroize for YkPivMgm { fn zeroize(&mut self) { self.0.zeroize(); } } impl Drop for YkPivMgm { fn drop(&mut self) { self.zeroize(); } } /// Get derived management key (MGM) pub unsafe fn ykpiv_util_get_derived_mgm( state: &mut YubiKey, pin: *const u8, pin_len: usize, mgm: *mut YkPivMgm, ) -> Result<(), ErrorKind> { let mut data = [0u8; YKPIV_OBJ_MAX_SIZE]; let mut cb_data: usize = data.len(); let mut p_item: *mut u8 = ptr::null_mut(); let mut cb_item: usize = 0; let mut res: ErrorKind = ErrorKind::Ok; if pin.is_null() || pin_len == 0 || mgm.is_null() { return Err(ErrorKind::GenericError); } if _ykpiv_begin_transaction(state) != ErrorKind::Ok { return Err(ErrorKind::PcscError); } if _ykpiv_ensure_application_selected(state) == ErrorKind::Ok { res = _read_metadata(state, 0x80u8, data.as_mut_ptr(), &mut cb_data); if res == ErrorKind::Ok { res = _get_metadata_item( data.as_mut_ptr(), cb_data, 0x82u8, &mut p_item, &mut cb_item, ); if res == ErrorKind::Ok { if cb_item != 16usize { if state.verbose != 0 { eprintln!( "derived mgm salt exists, but is incorrect size = {}", cb_item, ); } res = ErrorKind::GenericError; } else { let p5rc = pkcs5_pbkdf2_sha1( pin, pin_len, p_item, cb_item, 10000, (*mgm).0.as_mut_ptr(), (*mgm).0.len(), ); if p5rc != Pkcs5ErrorKind::Ok { if state.verbose != 0 { eprintln!("pbkdf2 failure, err = {:?}", p5rc); } res = ErrorKind::GenericError; } } } } } _ykpiv_end_transaction(state); match res { ErrorKind::Ok => Ok(()), e => Err(e), } } /// Get protected management key (MGM) pub unsafe fn ykpiv_util_get_protected_mgm( state: &mut YubiKey, mgm: *mut YkPivMgm, ) -> Result<(), ErrorKind> { let mut data = [0u8; YKPIV_OBJ_MAX_SIZE]; let mut cb_data: usize = data.len(); let mut p_item: *mut u8 = ptr::null_mut(); let mut cb_item: usize = 0; let mut res = ErrorKind::Ok; if mgm.is_null() { return Err(ErrorKind::GenericError); } if _ykpiv_begin_transaction(state) != ErrorKind::Ok { return Err(ErrorKind::PcscError); } if _ykpiv_ensure_application_selected(state) == ErrorKind::Ok { res = _read_metadata(state, 0x88u8, data.as_mut_ptr(), &mut cb_data); if res != ErrorKind::Ok { if state.verbose != 0 { eprintln!("could not read protected data, err = {:?}", res); } } else { res = _get_metadata_item( data.as_mut_ptr(), cb_data, 0x89u8, &mut p_item, &mut cb_item, ); if res != ErrorKind::Ok { if state.verbose != 0 { eprintln!( "could not read protected mgm from metadata, err = {}", res as (i32), ); } } else if cb_item != (*mgm).0.len() { if state.verbose != 0 { eprintln!( "protected data contains mgm, but is the wrong size = {}", cb_item, ); } res = ErrorKind::AuthenticationError; } else { memcpy( (*mgm).0.as_mut_ptr() as (*mut c_void), p_item as (*const c_void), cb_item, ); } } } data.zeroize(); _ykpiv_end_transaction(state); match res { ErrorKind::Ok => Ok(()), e => Err(e), } } /// Set protected management key (MGM) #[allow(clippy::cognitive_complexity)] pub unsafe fn ykpiv_util_set_protected_mgm( state: &mut YubiKey, mgm: *mut YkPivMgm, ) -> Result<(), ErrorKind> { let mut _currentBlock; let mut res: ErrorKind = ErrorKind::Ok; let mut ykrc: ErrorKind = ErrorKind::Ok; let mut prngrc: PRngErrorKind = PRngErrorKind::Ok; let mut f_generate: bool; let mut mgm_key = [0u8; 24]; let mut i: usize; let mut data = [0u8; YKPIV_OBJ_MAX_SIZE]; let mut cb_data = data.len(); let mut p_item: *mut u8 = ptr::null_mut(); let mut cb_item: usize = 0; let mut flags_1: u8 = 0; if mgm.is_null() { f_generate = true; } else { f_generate = true; memcpy( mgm_key.as_mut_ptr() as (*mut c_void), (*mgm).0.as_mut_ptr() as (*const c_void), (*mgm).0.len(), ); i = 0; loop { if i >= 24 { _currentBlock = 8; break; } if mgm_key[i] as (i32) != 0i32 { _currentBlock = 6; break; } i = i.wrapping_add(1); } if _currentBlock == 8 { } else { f_generate = false; } } if _ykpiv_begin_transaction(state) != ErrorKind::Ok { return Err(ErrorKind::PcscError); } if _ykpiv_ensure_application_selected(state) == ErrorKind::Ok { loop { if f_generate { prngrc = _ykpiv_prng_generate(mgm_key.as_mut_ptr(), mem::size_of::<[u8; 24]>()); if prngrc != PRngErrorKind::Ok { _currentBlock = 47; break; } } ykrc = ykpiv_set_mgmkey(state, mgm_key.as_mut_ptr()); if ykrc != ErrorKind::Ok { if ErrorKind::KeyError as (i32) != ykrc as (i32) { _currentBlock = 44; break; } } else { f_generate = false; } if !f_generate { _currentBlock = 16; break; } } if _currentBlock == 16 { if !mgm.is_null() { memcpy( (*mgm).0.as_mut_ptr() as (*mut c_void), mgm_key.as_mut_ptr() as (*const c_void), (*mgm).0.len(), ); } ykrc = _read_metadata(state, 0x88u8, data.as_mut_ptr(), &mut cb_data); if ykrc != ErrorKind::Ok { cb_data = 0; } ykrc = _set_metadata_item( data.as_mut_ptr(), &mut cb_data, CB_OBJ_MAX, 0x89, mgm_key.as_mut_ptr(), mgm_key.len(), ); if ykrc != ErrorKind::Ok { if state.verbose != 0 { eprintln!("could not set protected mgm item, err = {:?}", ykrc); _currentBlock = 26; } else { _currentBlock = 26; } } else { ykrc = _write_metadata(state, 0x88u8, data.as_mut_ptr(), cb_data); if ykrc != ErrorKind::Ok { if state.verbose != 0 { eprintln!("could not write protected data, err = {:?}", ykrc); _currentBlock = 51; } else { _currentBlock = 51; } } else { _currentBlock = 26; } } if _currentBlock != 51 { cb_data = YKPIV_OBJ_MAX_SIZE; ykrc = _read_metadata(state, 0x80u8, data.as_mut_ptr(), &mut cb_data); if ykrc != ErrorKind::Ok { cb_data = 0; } else { ykrc = _get_metadata_item( data.as_mut_ptr(), cb_data, 0x81u8, &mut p_item, &mut cb_item, ); if ykrc != ErrorKind::Ok && state.verbose != 0 { eprintln!("admin data exists, but flags are not present",); } if cb_item == 1 { // TODO(tarcieri): get rid of memcpy and pointers, replace with slices! #[allow(trivial_casts)] memcpy( &mut flags_1 as (*mut u8) as (*mut c_void), p_item as (*const c_void), cb_item, ); } else if state.verbose != 0 { eprintln!("admin data flags are an incorrect size = {}", cb_item,); } ykrc = _set_metadata_item( data.as_mut_ptr(), &mut cb_data, CB_OBJ_MAX, 0x82, ptr::null_mut(), 0, ); if ykrc != ErrorKind::Ok && state.verbose != 0 { eprintln!("could not unset derived mgm salt, err = {}", ykrc); } } flags_1 |= 0x2; ykrc = _set_metadata_item( data.as_mut_ptr(), &mut cb_data, CB_OBJ_MAX, 0x81, &mut flags_1, 1, ); if ykrc != ErrorKind::Ok { if state.verbose != 0 { eprintln!("could not set admin flags item, err = {}", ykrc); } } else { ykrc = _write_metadata(state, 0x80u8, data.as_mut_ptr(), cb_data); if ykrc != ErrorKind::Ok && state.verbose != 0 { eprintln!("could not write admin data, err = {}", ykrc); } } } } else if _currentBlock == 44 { if state.verbose != 0 { eprintln!("could not set new derived mgm key, err = {}", ykrc); } res = ykrc; } else { if state.verbose != 0 { eprintln!("could not generate new mgm, err = {:?}", prngrc); } res = ErrorKind::RandomnessError; } } data.zeroize(); mgm_key.zeroize(); _ykpiv_end_transaction(state); match res { ErrorKind::Ok => Ok(()), e => Err(e), } } /// Reset pub unsafe fn ykpiv_util_reset(state: &mut YubiKey) -> Result<(), ErrorKind> { 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, templ.as_ptr(), ptr::null(), 0, data.as_mut_ptr(), &mut recv_len, &mut sw, ); match (res, sw) { (ErrorKind::Ok, SW_SUCCESS) => Ok(()), _ => Err(ErrorKind::GenericError), } } /// Get object for slot pub fn ykpiv_util_slot_object(slot: u8) -> u32 { match slot { YKPIV_KEY_AUTHENTICATION => YKPIV_OBJ_AUTHENTICATION, YKPIV_KEY_SIGNATURE => YKPIV_OBJ_SIGNATURE, YKPIV_KEY_KEYMGM => YKPIV_OBJ_KEY_MANAGEMENT, YKPIV_KEY_CARDAUTH => YKPIV_OBJ_CARD_AUTH, YKPIV_KEY_ATTESTATION => YKPIV_OBJ_ATTESTATION, _ => { if slot >= YKPIV_KEY_RETIRED1 && (slot <= YKPIV_KEY_RETIRED20) { YKPIV_OBJ_RETIRED1 + (slot - YKPIV_KEY_RETIRED1) as u32 } else { 0 } } } } /// Read certificate unsafe fn _read_certificate( state: &mut YubiKey, slot: u8, buf: *mut u8, buf_len: *mut usize, ) -> ErrorKind { let mut ptr: *mut u8; let object_id = ykpiv_util_slot_object(slot) as i32; let mut len: usize = 0; if object_id == -1 { return ErrorKind::InvalidObject; } if _ykpiv_fetch_object(state, object_id, buf, buf_len) == ErrorKind::Ok { ptr = buf; if *buf_len < CB_OBJ_TAG_MIN { *buf_len = 0; return ErrorKind::Ok; } else if *{ let _old = ptr; ptr = ptr.offset(1); _old } == TAG_CERT { ptr = ptr.add(_ykpiv_get_length(ptr, &mut len)); if len > *buf_len - (ptr as isize - buf as isize) as usize { *buf_len = 0; return ErrorKind::Ok; } else { memmove(buf as (*mut c_void), ptr as (*const c_void), len); *buf_len = len; } } } else { *buf_len = 0; } ErrorKind::Ok } /// Write certificate unsafe fn _write_certificate( state: &mut YubiKey, slot: u8, data: *mut u8, data_len: usize, certinfo: u8, ) -> ErrorKind { let mut buf = [0u8; CB_OBJ_MAX]; let object_id = ykpiv_util_slot_object(slot) as i32; let mut offset: usize = 0; let mut req_len: usize; if object_id == -1 { return ErrorKind::InvalidObject; } if data.is_null() || data_len == 0 { if !data.is_null() || data_len != 0 { return ErrorKind::GenericError; } return _ykpiv_save_object(state, 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) { return ErrorKind::SizeError; } buf[offset] = TAG_CERT; offset += 1; offset += _ykpiv_set_length(buf.as_mut_ptr().add(offset), data_len); memcpy( buf.as_mut_ptr().add(offset) as (*mut c_void), data as (*const c_void), data_len, ); offset += data_len; // write compression info and LRC trailer buf[offset] = TAG_CERT_COMPRESS; buf[offset + 1] = 0x01; buf[offset + 2] = if certinfo == YKPIV_CERTINFO_GZIP { 0x01 } else { 0x00 }; buf[offset + 3] = TAG_CERT_LRC; buf[offset + 4] = 00; offset += 5; _ykpiv_save_object(state, object_id, buf.as_mut_ptr(), offset) } /// Get metadata item unsafe fn _get_metadata_item( data: *mut u8, cb_data: usize, tag: u8, pp_item: *mut *mut u8, pcb_item: *mut usize, ) -> ErrorKind { let mut p_temp: *mut u8 = data; let mut cb_temp: usize = 0; let mut tag_temp: u8; if data.is_null() || pp_item.is_null() || pcb_item.is_null() { return ErrorKind::GenericError; } *pp_item = ptr::null_mut(); *pcb_item = 0; while p_temp < data.add(cb_data) { tag_temp = *p_temp; p_temp = p_temp.add(1); if !_ykpiv_has_valid_length(p_temp, data.add(cb_data) as usize - p_temp as usize) { return ErrorKind::SizeError; } p_temp = p_temp.add(_ykpiv_get_length(p_temp, &mut cb_temp)); if tag_temp == tag { break; } p_temp = p_temp.add(cb_temp); } if p_temp < data.add(cb_data) { *pp_item = p_temp; *pcb_item = cb_temp; ErrorKind::Ok } else { ErrorKind::GenericError } } /// Get length size fn _get_length_size(length: usize) -> i32 { if length < 0x80 { 1 } else if length < 0xff { 2 } else { 3 } } /// Set metadata item unsafe fn _set_metadata_item( data: *mut u8, pcb_data: *mut usize, cb_data_max: usize, tag: u8, p_item: *mut u8, cb_item: usize, ) -> ErrorKind { let mut p_temp: *mut u8 = data; let mut cb_temp: usize = 0; let mut tag_temp: u8 = 0; let mut cb_len: usize = 0; let p_next: *mut u8; let cb_moved: isize; if data.is_null() || pcb_data.is_null() { return ErrorKind::GenericError; } while p_temp < data.add(*pcb_data) { tag_temp = *p_temp; p_temp = p_temp.add(1); cb_len = _ykpiv_get_length(p_temp, &mut cb_temp); p_temp = p_temp.add(cb_len); if tag_temp == tag { break; } p_temp = p_temp.add(cb_temp); } if tag_temp != tag { if cb_item == 0 { return ErrorKind::Ok; } p_temp = data.add(*pcb_data); cb_len = _get_length_size(cb_item) as (usize); if (*pcb_data).wrapping_add(cb_len).wrapping_add(cb_item) > cb_data_max { return ErrorKind::GenericError; } *p_temp = tag; p_temp = p_temp.add(1); p_temp = p_temp.add(_ykpiv_set_length(p_temp, cb_item)); memcpy(p_temp as (*mut c_void), p_item as (*const c_void), cb_item); *pcb_data += 1 + cb_len + cb_item; return ErrorKind::Ok; } if cb_temp == cb_item { memcpy(p_temp as (*mut c_void), p_item as (*const c_void), cb_item); return ErrorKind::Ok; } p_next = p_temp.add(cb_temp); cb_moved = cb_item as (isize) - cb_temp as (isize) + (if cb_item != 0 { _get_length_size(cb_item) } else { -1 } as (isize) - cb_len as (isize)); if (*pcb_data + cb_moved as usize) > cb_data_max { return ErrorKind::GenericError; } memmove( p_next.offset(cb_moved) as (*mut c_void), p_next as (*const c_void), (*pcb_data).wrapping_sub( ((p_next as (isize)).wrapping_sub(data as (isize)) / mem::size_of::() as (isize)) as (usize), ), ); *pcb_data = (*pcb_data).wrapping_add(cb_moved as (usize)); if cb_item != 0 { p_temp = p_temp.offset(-(cb_len as (isize))); p_temp = p_temp.add(_ykpiv_set_length(p_temp, cb_item)); memcpy(p_temp as (*mut c_void), p_item as (*const c_void), cb_item); } ErrorKind::Ok } /// Read metadata unsafe fn _read_metadata( state: &mut YubiKey, tag: u8, data: *mut u8, pcb_data: *mut usize, ) -> ErrorKind { let mut p_temp: *mut u8; let mut cb_temp: usize; let res: ErrorKind; if data.is_null() || pcb_data.is_null() || YKPIV_OBJ_MAX_SIZE > *pcb_data { return ErrorKind::GenericError; } let obj_id = match tag { TAG_ADMIN => YKPIV_OBJ_ADMIN_DATA, TAG_PROTECTED => YKPIV_OBJ_PRINTED, _ => return ErrorKind::InvalidObject, } as i32; cb_temp = *pcb_data; *pcb_data = 0; res = _ykpiv_fetch_object(state, obj_id, data, &mut cb_temp); if res != ErrorKind::Ok { return res; } if cb_temp < CB_OBJ_TAG_MIN { return ErrorKind::GenericError; } p_temp = data; if tag as (i32) != *{ let _old = p_temp; p_temp = p_temp.offset(1); _old } as (i32) { return ErrorKind::GenericError; } p_temp = p_temp.add(_ykpiv_get_length(p_temp, pcb_data)); if *pcb_data > cb_temp - (p_temp as isize - data as isize) as usize { *pcb_data = 0; return ErrorKind::GenericError; } memmove(data as (*mut c_void), p_temp as (*const c_void), *pcb_data); ErrorKind::Ok } /// Write metadata unsafe fn _write_metadata( state: &mut YubiKey, tag: u8, data: *mut u8, cb_data: usize, ) -> ErrorKind { 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 { return ErrorKind::GenericError; } let obj_id = match tag { TAG_ADMIN => YKPIV_OBJ_ADMIN_DATA, TAG_PROTECTED => YKPIV_OBJ_PRINTED, _ => return ErrorKind::InvalidObject, } as i32; if data.is_null() || cb_data == 0 { return _ykpiv_save_object(state, obj_id, ptr::null_mut(), 0); } *{ let _old = p_temp; p_temp = p_temp.offset(1); _old } = tag; p_temp = p_temp.add(_ykpiv_set_length(p_temp, cb_data)); 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, obj_id, buf.as_mut_ptr(), ((p_temp as (isize)).wrapping_sub(buf.as_mut_ptr() as (isize)) / mem::size_of::() as (isize)) as (usize), ) }