157 lines
5.3 KiB
Rust
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()
|
|
}
|
|
}
|