Files
yubikey.rs/src/msroots.rs
T
2019-11-28 23:43:02 +00:00

157 lines
5.3 KiB
Rust

//! `msroots`: PKCS#7 formatted certificate store for enterprise trusted roots.
//!
//! This `msroots` file contains a bag of certificates with empty content and
//! an empty signature, allowing an enterprise root certificate truststore to
//! be written to and read from a YubiKey.
//!
//! For more information, see:
//! <https://docs.microsoft.com/en-us/windows-hardware/drivers/smartcard/developer-guidelines#-interoperability-with-msroots>
// 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.
use crate::{consts::*, error::Error, serialization::*, yubikey::YubiKey};
use log::error;
/// `msroots` file: PKCS#7-formatted certificate store for enterprise trust roots
pub struct MsRoots(Vec<u8>);
impl MsRoots {
/// Initialize a local certificate struct from the given bytebuffer
pub fn new(msroots: impl AsRef<[u8]>) -> Result<Self, Error> {
Ok(MsRoots(msroots.as_ref().into()))
}
/// Read `msroots` file from YubiKey
pub fn read(yubikey: &mut YubiKey) -> Result<Option<Self>, Error> {
let cb_data = yubikey.obj_size_max();
let txn = yubikey.begin_transaction()?;
// allocate first page
let mut data = Vec::with_capacity(cb_data);
for object_id in YKPIV_OBJ_MSROOTS1..YKPIV_OBJ_MSROOTS5 {
let buf = txn.fetch_object(object_id)?;
let cb_buf = buf.len();
if cb_buf < CB_OBJ_TAG_MIN {
return Ok(None);
}
let tag = buf[0];
if (TAG_MSROOTS_MID != tag || YKPIV_OBJ_MSROOTS5 == object_id)
&& (TAG_MSROOTS_END != tag)
{
// the current object doesn't contain a valid part of a msroots file
// treat condition as object isn't found
return Ok(None);
}
let mut len: usize = 0;
let offset = 1 + get_length(&buf[1..], &mut len);
// check that decoded length represents object contents
if len > cb_buf - offset {
return Ok(None);
}
data.extend_from_slice(&buf[offset..offset + len]);
if tag == TAG_MSROOTS_END {
break;
}
}
MsRoots::new(&data).map(Some).map_err(|e| {
error!("error parsing msroots: {:?}", e);
e
})
}
/// Write `msroots` file to YubiKey
pub fn write(&self, yubikey: &mut YubiKey) -> Result<(), Error> {
let mut buf = [0u8; CB_OBJ_MAX];
let mut offset: usize;
let mut data_offset: usize = 0;
let mut data_chunk: usize;
let data = &self.0;
let data_len = data.len();
let n_objs: usize;
let cb_obj_max = yubikey.obj_size_max();
let txn = yubikey.begin_transaction()?;
if data_len == 0 {
return txn.save_object(YKPIV_OBJ_MSROOTS1, &[]);
}
// Calculate number of objects required to store blob
n_objs = (data_len / (cb_obj_max - CB_OBJ_TAG_MAX)) + 1;
if n_objs > 5 {
return Err(Error::SizeError);
}
for i in 0..n_objs {
offset = 0;
data_chunk = if cb_obj_max - CB_OBJ_TAG_MAX < data_len - data_offset {
cb_obj_max - CB_OBJ_TAG_MAX
} else {
data_len - data_offset
};
buf[offset] = if i == n_objs - 1 {
TAG_MSROOTS_END
} else {
TAG_MSROOTS_MID
};
offset += 1;
offset += set_length(&mut buf[offset..], data_chunk);
buf[offset..].copy_from_slice(&data[data_offset..(data_offset + data_chunk)]);
offset += data_chunk;
txn.save_object(YKPIV_OBJ_MSROOTS1 + i as u32, &buf[..offset])?;
data_offset += data_chunk;
}
Ok(())
}
}
impl AsRef<[u8]> for MsRoots {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}