2408 lines
71 KiB
Rust
2408 lines
71 KiB
Rust
//! Utility functions
|
|
|
|
// Adapted from yubico-piv-tool:
|
|
// <https://github.com/Yubico/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::<YkPivKey>()
|
|
.wrapping_add(cb_buf)
|
|
.wrapping_sub(1)
|
|
> cb_data.wrapping_sub(offset)
|
|
{
|
|
(if mem::size_of::<YkPivKey>()
|
|
.wrapping_add(cb_buf)
|
|
.wrapping_sub(1)
|
|
.wrapping_sub(cb_data.wrapping_sub(offset))
|
|
> CB_PAGE
|
|
{
|
|
mem::size_of::<YkPivKey>()
|
|
.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::<YkPivKey>()
|
|
.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::<YkPivContainer>());
|
|
}
|
|
}
|
|
}
|
|
|
|
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::<YkPivContainer>());
|
|
|
|
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 <https://www.yubico.com/support/security-advisories/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::<u8>() 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::<u8>() as (isize)) as (usize),
|
|
)
|
|
}
|