Files
yubikey.rs/src/internal.rs
T
2019-11-21 00:44:49 +00:00

304 lines
8.8 KiB
Rust

//! Internal functions (mostly 3DES)
// TODO(tarcieri): replace OpenSSL extern "C" invocations with `des` crate
// - crate: https://crates.io/crates/des
// - docs: https://docs.rs/des/0
// 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.
// TODO(tarcieri): investigate and remove dead code
#![allow(non_upper_case_globals, dead_code)]
#![allow(clippy::missing_safety_doc)]
use crate::consts::*;
use des::{
block_cipher_trait::{generic_array::GenericArray, BlockCipher},
TdesEde3,
};
use libc::{c_char, c_int, fclose, feof, fgets, fopen, getenv, sscanf, strcasecmp, strcmp};
use std::ffi::{CStr, CString};
use zeroize::Zeroize;
/// 3DES keys. The three subkeys are concatenated.
pub struct DesKey([u8; DES_LEN_3DES]);
impl DesKey {
pub fn from_bytes(bytes: [u8; DES_LEN_3DES]) -> Self {
DesKey(bytes)
}
pub fn write(&self, out: &mut [u8]) {
out.copy_from_slice(&self.0);
}
}
impl AsRef<[u8; 24]> for DesKey {
fn as_ref(&self) -> &[u8; 24] {
&self.0
}
}
impl Zeroize for DesKey {
fn zeroize(&mut self) {
self.0.zeroize();
}
}
impl Drop for DesKey {
fn drop(&mut self) {
self.zeroize();
}
}
/// Encrypt with DES key
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn des_encrypt(key: &DesKey, input: &[u8; DES_LEN_DES], output: &mut [u8; DES_LEN_DES]) {
output.copy_from_slice(input);
TdesEde3::new(GenericArray::from_slice(&key.0))
.encrypt_block(GenericArray::from_mut_slice(output));
}
/// Decrypt with DES key
#[allow(clippy::trivially_copy_pass_by_ref)]
pub fn des_decrypt(key: &DesKey, input: &[u8; DES_LEN_DES], output: &mut [u8; DES_LEN_DES]) {
output.copy_from_slice(input);
TdesEde3::new(GenericArray::from_slice(&key.0))
.encrypt_block(GenericArray::from_mut_slice(output));
}
/// Is the given DES key weak?
pub fn yk_des_is_weak_key(key: &[u8; DES_LEN_3DES]) -> bool {
/// Weak and semi weak keys as taken from
/// %A D.W. Davies
/// %A W.L. Price
/// %T Security for Computer Networks
/// %I John Wiley & Sons
/// %D 1984
const weak_keys: [[u8; DES_LEN_DES]; 16] = [
// weak keys
[0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01],
[0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE],
[0x1F, 0x1F, 0x1F, 0x1F, 0x0E, 0x0E, 0x0E, 0x0E],
[0xE0, 0xE0, 0xE0, 0xE0, 0xF1, 0xF1, 0xF1, 0xF1],
// semi-weak keys
[0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE],
[0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01, 0xFE, 0x01],
[0x1F, 0xE0, 0x1F, 0xE0, 0x0E, 0xF1, 0x0E, 0xF1],
[0xE0, 0x1F, 0xE0, 0x1F, 0xF1, 0x0E, 0xF1, 0x0E],
[0x01, 0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1],
[0xE0, 0x01, 0xE0, 0x01, 0xF1, 0x01, 0xF1, 0x01],
[0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E, 0xFE],
[0xFE, 0x1F, 0xFE, 0x1F, 0xFE, 0x0E, 0xFE, 0x0E],
[0x01, 0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E],
[0x1F, 0x01, 0x1F, 0x01, 0x0E, 0x01, 0x0E, 0x01],
[0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1, 0xFE],
[0xFE, 0xE0, 0xFE, 0xE0, 0xFE, 0xF1, 0xFE, 0xF1],
];
// set odd parity of key
let mut tmp = [0u8; DES_LEN_3DES];
for i in 0..DES_LEN_3DES {
// count number of set bits in byte, excluding the low-order bit - SWAR method
let mut c = key[i] & 0xFE;
c = (c & 0x55) + ((c >> 1) & 0x55);
c = (c & 0x33) + ((c >> 2) & 0x33);
c = (c & 0x0F) + ((c >> 4) & 0x0F);
// if count is even, set low key bit to 1, otherwise 0
tmp[i] = (key[i] & 0xFE) | (if c & 0x01 == 0x01 { 0x00 } else { 0x01 });
}
// check odd parity key against table by DES key block
let mut rv = false;
for weak_key in weak_keys.iter() {
if weak_key == &tmp[0..DES_LEN_DES]
|| weak_key == &tmp[DES_LEN_DES..2 * DES_LEN_DES]
|| weak_key == &tmp[2 * DES_LEN_DES..3 * DES_LEN_DES]
{
rv = true;
break;
}
}
tmp.zeroize();
rv
}
/// PKCS#5 error types
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
#[repr(i32)]
pub enum Pkcs5ErrorKind {
/// OK
Ok = 0,
/// General error
GeneralError = -1,
}
/// Strip whitespace
// TODO(tarcieri): implement this
pub unsafe fn _strip_ws(sz: *mut c_char) -> *mut c_char {
sz
}
/// Source of how a setting was configured
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum SettingSource {
/// User-specified setting
User,
/// Admin-specified setting
Admin,
/// Default setting
Default,
}
/// Setting booleans
#[derive(Copy, Clone, Debug)]
pub struct SettingBool {
/// Boolean value
pub value: bool,
/// Source of the configuration setting (user/admin/default)
pub source: SettingSource,
}
/// Get a boolean config value
pub unsafe fn _get_bool_config(sz_setting: *const c_char) -> SettingBool {
let mut setting: SettingBool = SettingBool {
value: false,
source: SettingSource::Default,
};
let mut sz_line = [0u8; 256];
let mut psz_name: *mut c_char;
let mut psz_value: *mut c_char;
let mut sz_name = [0u8; 256];
let mut sz_value = [0u8; 256];
let pf = fopen(
b"/etc/yubico/yubikeypiv.conf\0".as_ptr() as *const c_char,
b"r\0".as_ptr() as *const c_char,
);
if pf.is_null() {
return setting;
}
while feof(pf) == 0 {
if fgets(
sz_line.as_mut_ptr() as *mut c_char,
sz_line.len() as c_int,
pf,
)
.is_null()
{
continue;
}
if sz_line[0] == b'#' {
continue;
}
if sz_line[0] == b'\r' {
continue;
}
if sz_line[0] == b'\n' {
continue;
}
if sscanf(
sz_line.as_ptr() as *const c_char,
b"%255[^=]=%255s\0".as_ptr() as *const c_char,
sz_name.as_mut_ptr(),
sz_value.as_mut_ptr(),
) != 2
{
continue;
}
psz_name = _strip_ws(sz_name.as_mut_ptr() as *mut c_char);
if strcasecmp(psz_name, sz_setting) != 0 {
continue;
}
psz_value = _strip_ws(sz_value.as_mut_ptr() as *mut c_char);
setting.source = SettingSource::Admin;
setting.value = strcmp(psz_value, b"1\0".as_ptr() as *const c_char) == 0
|| strcasecmp(psz_value, b"true\0".as_ptr() as *const c_char) == 0;
}
fclose(pf);
setting
}
/// Get a setting boolean from an environment variable
pub unsafe fn _get_bool_env(sz_setting: *const c_char) -> SettingBool {
let mut setting: SettingBool = SettingBool {
value: false,
source: SettingSource::Default,
};
let sz_name = CString::new(format!(
"YUBIKEY_PIV_{}",
CStr::from_ptr(sz_setting).to_string_lossy()
))
.unwrap();
let psz_value = getenv(sz_name.as_ptr());
if !psz_value.is_null() {
setting.source = SettingSource::User;
setting.value = strcmp(psz_value, b"1\0".as_ptr() as *const c_char) == 0
|| strcasecmp(psz_value, b"true\0".as_ptr() as *const c_char) == 0;
}
setting
}
/// Get a setting boolean
pub unsafe fn setting_get_bool(sz_setting: *const c_char, def: bool) -> SettingBool {
let mut setting = _get_bool_config(sz_setting);
if setting.source == SettingSource::Default {
setting = _get_bool_env(sz_setting);
}
if setting.source == SettingSource::Default {
setting.value = def;
}
setting
}