Flatten API (#274)
Re-exports types from the toplevel instead of placing them in individual modules (often which only contain one type). This makes the API easier for users to navigate, while still retaining the same module structure internally. Additionally, this commit uses the `uuid` crate for modeling UUIDs.
This commit is contained in:
committed by
GitHub
parent
1228d16439
commit
1765e11bc0
Generated
+10
@@ -887,6 +887,15 @@ version = "0.2.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.2.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
@@ -998,6 +1007,7 @@ dependencies = [
|
|||||||
"sha2",
|
"sha2",
|
||||||
"subtle",
|
"subtle",
|
||||||
"subtle-encoding",
|
"subtle-encoding",
|
||||||
|
"uuid",
|
||||||
"x509",
|
"x509",
|
||||||
"x509-parser",
|
"x509-parser",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ sha-1 = "0.9"
|
|||||||
sha2 = "0.9"
|
sha2 = "0.9"
|
||||||
subtle = "2"
|
subtle = "2"
|
||||||
subtle-encoding = "0.5"
|
subtle-encoding = "0.5"
|
||||||
|
uuid = { version = "0.8", features = ["v4"] }
|
||||||
x509 = "0.2"
|
x509 = "0.2"
|
||||||
x509-parser = "0.9"
|
x509-parser = "0.9"
|
||||||
zeroize = "1"
|
zeroize = "1"
|
||||||
@@ -57,3 +58,4 @@ untested = []
|
|||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
rustdoc-args = ["--cfg", "docsrs"]
|
||||||
|
|||||||
@@ -53,41 +53,6 @@ an experimental stage and may still contain high-severity issues.
|
|||||||
|
|
||||||
USE AT YOUR OWN RISK!
|
USE AT YOUR OWN RISK!
|
||||||
|
|
||||||
## Status
|
|
||||||
|
|
||||||
This project is a largely incomplete work-in-progress. So far the only
|
|
||||||
functionality which has actually been tested is connecting to Yubikeys.
|
|
||||||
|
|
||||||
If you're interested helping test functionality, the table below documents
|
|
||||||
the current status of the project and relevant GitHub issues for various
|
|
||||||
functions of the YubiKey:
|
|
||||||
|
|
||||||
| | Module | Issue | Description |
|
|
||||||
|----|---------------|-------|-------------|
|
|
||||||
| 🚧 | `yubikey` | [#20] | Core functionality: auth, keys, PIN/PUK, encrypt, sign, attest |
|
|
||||||
| 🚧 | `cccid` | [#21] | Cardholder Capability Container (CCC) IDs |
|
|
||||||
| 🚧️ | `certificate` | [#22] | Certificates for stored keys |
|
|
||||||
| 🚧 | `chuid` | [#23] | Cardholder Unique Identifier (CHUID) |
|
|
||||||
| ✅️ | `config` | [#24] | Support for reading on-key configuration |
|
|
||||||
| 🚧 | `key` | [#26] | Crypto key management: list, generate, import |
|
|
||||||
| 🚧 | `mgm` | [#26] | Management Key (MGM) support: set, get, derive |
|
|
||||||
| ⚠️ | `mscmap` | [#25] | MS Container Map Records |
|
|
||||||
| ⚠️ | `msroots` | [#28] | `msroots` file: PKCS#7 formatted certificate store for enterprise trusted roots |
|
|
||||||
|
|
||||||
Legend:
|
|
||||||
|
|
||||||
| | Description |
|
|
||||||
|----|------------------------------------|
|
|
||||||
| ✅ | Working |
|
|
||||||
| 🚧 | Testing and validation in progress |
|
|
||||||
| ⚠️ | Untested support |
|
|
||||||
|
|
||||||
NOTE: Commands marked ⚠️ are disabled by default as they have have not been properly tested and may contain bugs or
|
|
||||||
not work at all. USE AT YOUR OWN RISK!
|
|
||||||
|
|
||||||
Enable the `untested` feature in your `Cargo.toml` to enable features marked ⚠️
|
|
||||||
above.
|
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
To run the full test suite, you'll need a connected YubiKey NEO/4/5 device in
|
To run the full test suite, you'll need a connected YubiKey NEO/4/5 device in
|
||||||
|
|||||||
+22
-29
@@ -32,15 +32,12 @@
|
|||||||
|
|
||||||
use crate::{Error, Result, YubiKey};
|
use crate::{Error, Result, YubiKey};
|
||||||
use getrandom::getrandom;
|
use getrandom::getrandom;
|
||||||
use std::fmt::{self, Debug, Display};
|
use std::{
|
||||||
|
fmt::{self, Debug, Display},
|
||||||
|
str,
|
||||||
|
};
|
||||||
use subtle_encoding::hex;
|
use subtle_encoding::hex;
|
||||||
|
|
||||||
/// CCCID size
|
|
||||||
pub const CCCID_SIZE: usize = 14;
|
|
||||||
|
|
||||||
/// CCC size
|
|
||||||
pub const CCC_SIZE: usize = 51;
|
|
||||||
|
|
||||||
/// CCCID offset
|
/// CCCID offset
|
||||||
const CCC_ID_OFFS: usize = 9;
|
const CCC_ID_OFFS: usize = 9;
|
||||||
|
|
||||||
@@ -62,28 +59,34 @@ const CCC_TMPL: &[u8] = &[
|
|||||||
0x00, 0xfe, 0x00,
|
0x00, 0xfe, 0x00,
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Cardholder Capability Container (CCC) Identifier Card ID
|
/// Cardholder Capability Container (CCC) Identifier Card ID.
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct CardId(pub [u8; CCCID_SIZE]);
|
pub struct CardId(pub [u8; Self::BYTE_SIZE]);
|
||||||
|
|
||||||
impl CardId {
|
impl CardId {
|
||||||
|
/// CCCID size in bytes
|
||||||
|
pub const BYTE_SIZE: usize = 14;
|
||||||
|
|
||||||
/// Generate a random CCC Card ID
|
/// Generate a random CCC Card ID
|
||||||
pub fn generate() -> Result<Self> {
|
pub fn generate() -> Result<Self> {
|
||||||
let mut id = [0u8; CCCID_SIZE];
|
let mut id = [0u8; Self::BYTE_SIZE];
|
||||||
getrandom(&mut id).map_err(|_| Error::RandomnessError)?;
|
getrandom(&mut id).map_err(|_| Error::RandomnessError)?;
|
||||||
Ok(Self(id))
|
Ok(Self(id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cardholder Capability Container (CCC) Identifier
|
/// Cardholder Capability Container (CCC) Identifier.
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct Ccc(pub [u8; CCC_SIZE]);
|
pub struct Ccc(pub [u8; Self::BYTE_SIZE]);
|
||||||
|
|
||||||
impl Ccc {
|
impl Ccc {
|
||||||
|
/// CCC size in bytes
|
||||||
|
pub const BYTE_SIZE: usize = 51;
|
||||||
|
|
||||||
/// Return CardId component of CCC
|
/// Return CardId component of CCC
|
||||||
pub fn card_id(&self) -> Result<CardId> {
|
pub fn card_id(&self) -> Result<CardId> {
|
||||||
let mut cccid = [0u8; CCCID_SIZE];
|
let mut cccid = [0u8; CardId::BYTE_SIZE];
|
||||||
cccid.copy_from_slice(&self.0[CCC_ID_OFFS..(CCC_ID_OFFS + CCCID_SIZE)]);
|
cccid.copy_from_slice(&self.0[CCC_ID_OFFS..(CCC_ID_OFFS + CardId::BYTE_SIZE)]);
|
||||||
Ok(CardId(cccid))
|
Ok(CardId(cccid))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,8 +99,8 @@ impl Ccc {
|
|||||||
return Err(Error::GenericError);
|
return Err(Error::GenericError);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ccc = [0u8; CCC_SIZE];
|
let mut ccc = [0u8; Self::BYTE_SIZE];
|
||||||
ccc.copy_from_slice(&response[0..CCC_SIZE]);
|
ccc.copy_from_slice(&response[0..Self::BYTE_SIZE]);
|
||||||
Ok(Self(ccc))
|
Ok(Self(ccc))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,18 +115,8 @@ impl Ccc {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Ccc {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "CCC({:?})", &self.0[..])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Ccc {
|
impl Display for Ccc {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(
|
write!(f, "{}", str::from_utf8(&hex::encode(&self.0[..])).unwrap())
|
||||||
f,
|
|
||||||
"{}",
|
|
||||||
String::from_utf8(hex::encode(&self.0[..])).unwrap()
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,4 +1,4 @@
|
|||||||
//! YubiKey Certificates
|
//! X.509 certificate support.
|
||||||
|
|
||||||
// Adapted from yubico-piv-tool:
|
// Adapted from yubico-piv-tool:
|
||||||
// <https://github.com/Yubico/yubico-piv-tool/>
|
// <https://github.com/Yubico/yubico-piv-tool/>
|
||||||
|
|||||||
+31
-59
@@ -31,21 +31,13 @@
|
|||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
use crate::{Error, Result, YubiKey};
|
use crate::{Error, Result, YubiKey};
|
||||||
use getrandom::getrandom;
|
use std::{
|
||||||
use std::fmt::{self, Debug, Display};
|
convert::TryInto,
|
||||||
|
fmt::{self, Debug, Display},
|
||||||
|
str,
|
||||||
|
};
|
||||||
use subtle_encoding::hex;
|
use subtle_encoding::hex;
|
||||||
|
use uuid::Uuid;
|
||||||
/// CHUID size
|
|
||||||
pub const CHUID_SIZE: usize = 59;
|
|
||||||
|
|
||||||
/// CARDID size
|
|
||||||
pub const CARDID_SIZE: usize = 16;
|
|
||||||
|
|
||||||
/// FASC-N component size
|
|
||||||
pub const FASCN_SIZE: usize = 25;
|
|
||||||
|
|
||||||
/// Expiration size
|
|
||||||
pub const EXPIRATION_SIZE: usize = 8;
|
|
||||||
|
|
||||||
/// FASC-N offset
|
/// FASC-N offset
|
||||||
const CHUID_FASCN_OFFS: usize = 2;
|
const CHUID_FASCN_OFFS: usize = 2;
|
||||||
@@ -81,46 +73,38 @@ const CHUID_TMPL: &[u8] = &[
|
|||||||
0x30, 0x33, 0x30, 0x30, 0x31, 0x30, 0x31, 0x3e, 0x00, 0xfe, 0x00,
|
0x30, 0x33, 0x30, 0x30, 0x31, 0x30, 0x31, 0x3e, 0x00, 0xfe, 0x00,
|
||||||
];
|
];
|
||||||
|
|
||||||
/// Cardholder Unique Identifier (CHUID) Card UUID/GUID value
|
/// Cardholder Unique Identifier (CHUID).
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct Uuid(pub [u8; CARDID_SIZE]);
|
pub struct ChuId(pub [u8; Self::BYTE_SIZE]);
|
||||||
|
|
||||||
impl Uuid {
|
|
||||||
/// Generate a random Cardholder Unique Identifier (CHUID) UUID
|
|
||||||
pub fn generate() -> Result<Self> {
|
|
||||||
let mut id = [0u8; CARDID_SIZE];
|
|
||||||
getrandom(&mut id).map_err(|_| Error::RandomnessError)?;
|
|
||||||
Ok(Self(id))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Cardholder Unique Identifier (CHUID)
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct ChuId(pub [u8; CHUID_SIZE]);
|
|
||||||
|
|
||||||
impl ChuId {
|
impl ChuId {
|
||||||
|
/// CHUID size in bytes
|
||||||
|
pub const BYTE_SIZE: usize = 59;
|
||||||
|
|
||||||
|
/// FASC-N component size
|
||||||
|
pub const FASCN_SIZE: usize = 25;
|
||||||
|
|
||||||
|
/// Expiration size
|
||||||
|
pub const EXPIRATION_SIZE: usize = 8;
|
||||||
|
|
||||||
/// Return FASC-N component of CHUID
|
/// Return FASC-N component of CHUID
|
||||||
pub fn fascn(&self) -> Result<[u8; FASCN_SIZE]> {
|
pub fn fascn(&self) -> [u8; Self::FASCN_SIZE] {
|
||||||
let mut fascn = [0u8; FASCN_SIZE];
|
self.0[CHUID_FASCN_OFFS..(CHUID_FASCN_OFFS + Self::FASCN_SIZE)]
|
||||||
fascn.copy_from_slice(&self.0[CHUID_FASCN_OFFS..(CHUID_FASCN_OFFS + FASCN_SIZE)]);
|
.try_into()
|
||||||
Ok(fascn)
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return Card UUID/GUID component of CHUID
|
/// Return Card UUID/GUID component of CHUID
|
||||||
pub fn uuid(&self) -> Result<[u8; CARDID_SIZE]> {
|
pub fn uuid(&self) -> Uuid {
|
||||||
let mut uuid = [0u8; CARDID_SIZE];
|
Uuid::from_slice(&self.0[CHUID_GUID_OFFS..(CHUID_GUID_OFFS + 16)]).unwrap()
|
||||||
uuid.copy_from_slice(&self.0[CHUID_GUID_OFFS..(CHUID_GUID_OFFS + CARDID_SIZE)]);
|
|
||||||
Ok(uuid)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return expiration date component of CHUID
|
/// Return expiration date component of CHUID
|
||||||
// TODO(tarcieri): parse expiration?
|
// TODO(tarcieri): parse expiration?
|
||||||
pub fn expiration(&self) -> Result<[u8; EXPIRATION_SIZE]> {
|
pub fn expiration(&self) -> [u8; Self::EXPIRATION_SIZE] {
|
||||||
let mut expiration = [0u8; EXPIRATION_SIZE];
|
self.0[CHUID_EXPIRATION_OFFS..(CHUID_EXPIRATION_OFFS + Self::EXPIRATION_SIZE)]
|
||||||
expiration.copy_from_slice(
|
.try_into()
|
||||||
&self.0[CHUID_EXPIRATION_OFFS..(CHUID_EXPIRATION_OFFS + EXPIRATION_SIZE)],
|
.unwrap()
|
||||||
);
|
|
||||||
Ok(expiration)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get Cardholder Unique Identifier (CHUID)
|
/// Get Cardholder Unique Identifier (CHUID)
|
||||||
@@ -132,17 +116,15 @@ impl ChuId {
|
|||||||
return Err(Error::GenericError);
|
return Err(Error::GenericError);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut chuid = [0u8; CHUID_SIZE];
|
Ok(ChuId(response[..Self::BYTE_SIZE].try_into().unwrap()))
|
||||||
chuid.copy_from_slice(&response[0..CHUID_SIZE]);
|
|
||||||
let retval = ChuId { 0: chuid };
|
|
||||||
Ok(retval)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set Cardholder Unique Identifier (CHUID)
|
/// Set Cardholder Unique Identifier (CHUID)
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn set(&self, yubikey: &mut YubiKey) -> Result<()> {
|
pub fn set(&self, yubikey: &mut YubiKey) -> Result<()> {
|
||||||
let mut buf = CHUID_TMPL.to_vec();
|
let mut buf = CHUID_TMPL.to_vec();
|
||||||
buf[0..self.0.len()].copy_from_slice(&self.0);
|
buf[..Self::BYTE_SIZE].copy_from_slice(&self.0);
|
||||||
|
|
||||||
let txn = yubikey.begin_transaction()?;
|
let txn = yubikey.begin_transaction()?;
|
||||||
txn.save_object(OBJ_CHUID, &buf)
|
txn.save_object(OBJ_CHUID, &buf)
|
||||||
@@ -151,16 +133,6 @@ impl ChuId {
|
|||||||
|
|
||||||
impl Display for ChuId {
|
impl Display for ChuId {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(
|
write!(f, "{}", str::from_utf8(&hex::encode(&self.0[..])).unwrap())
|
||||||
f,
|
|
||||||
"{}",
|
|
||||||
String::from_utf8(hex::encode(&self.0[..])).unwrap()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for ChuId {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "CHUID({:?})", &self.0[..])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-11
@@ -46,28 +46,28 @@ use std::{
|
|||||||
const CB_ADMIN_TIMESTAMP: usize = 0x04;
|
const CB_ADMIN_TIMESTAMP: usize = 0x04;
|
||||||
const PROTECTED_FLAGS_1_PUK_NOBLOCK: u8 = 0x01;
|
const PROTECTED_FLAGS_1_PUK_NOBLOCK: u8 = 0x01;
|
||||||
|
|
||||||
/// Config
|
/// YubiKey configuration.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
/// Protected data available
|
/// Protected data available
|
||||||
protected_data_available: bool,
|
pub protected_data_available: bool,
|
||||||
|
|
||||||
/// PUK blocked
|
/// PUK blocked
|
||||||
puk_blocked: bool,
|
pub puk_blocked: bool,
|
||||||
|
|
||||||
/// No block on upgrade
|
/// No block on upgrade
|
||||||
puk_noblock_on_upgrade: bool,
|
pub puk_noblock_on_upgrade: bool,
|
||||||
|
|
||||||
/// PIN last changed
|
/// PIN last changed
|
||||||
pin_last_changed: Option<SystemTime>,
|
pub pin_last_changed: Option<SystemTime>,
|
||||||
|
|
||||||
/// MGM type
|
/// MGM type
|
||||||
mgm_type: MgmType,
|
pub mgm_type: MgmType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
/// Get YubiKey config
|
/// Get YubiKey config.
|
||||||
pub fn get(yubikey: &mut YubiKey) -> Result<Config> {
|
pub(crate) fn get(yubikey: &mut YubiKey) -> Result<Config> {
|
||||||
let mut config = Config {
|
let mut config = Config {
|
||||||
protected_data_available: false,
|
protected_data_available: false,
|
||||||
puk_blocked: false,
|
puk_blocked: false,
|
||||||
@@ -129,9 +129,7 @@ impl Config {
|
|||||||
|
|
||||||
if protected_data.get_item(TAG_PROTECTED_MGM).is_ok() {
|
if protected_data.get_item(TAG_PROTECTED_MGM).is_ok() {
|
||||||
if config.mgm_type != MgmType::Protected {
|
if config.mgm_type != MgmType::Protected {
|
||||||
error!(
|
error!("conflicting MGM key types: protected MGM exists");
|
||||||
"conflicting types of mgm key administration configured: protected MGM exists"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always favor protected MGM
|
// Always favor protected MGM
|
||||||
|
|||||||
+1
-1
@@ -35,7 +35,7 @@ use std::fmt::{self, Display};
|
|||||||
/// Result type with [`Error`].
|
/// Result type with [`Error`].
|
||||||
pub type Result<T> = core::result::Result<T, Error>;
|
pub type Result<T> = core::result::Result<T, Error>;
|
||||||
|
|
||||||
/// Kinds of errors
|
/// Kinds of errors.
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
|||||||
+19
-8
@@ -350,10 +350,13 @@ pub const SLOTS: [SlotId; 24] = [
|
|||||||
pub enum AlgorithmId {
|
pub enum AlgorithmId {
|
||||||
/// 1024-bit RSA.
|
/// 1024-bit RSA.
|
||||||
Rsa1024,
|
Rsa1024,
|
||||||
|
|
||||||
/// 2048-bit RSA.
|
/// 2048-bit RSA.
|
||||||
Rsa2048,
|
Rsa2048,
|
||||||
|
|
||||||
/// ECDSA with the NIST P256 curve.
|
/// ECDSA with the NIST P256 curve.
|
||||||
EccP256,
|
EccP256,
|
||||||
|
|
||||||
/// ECDSA with the NIST P384 curve.
|
/// ECDSA with the NIST P384 curve.
|
||||||
EccP384,
|
EccP384,
|
||||||
}
|
}
|
||||||
@@ -390,6 +393,7 @@ impl AlgorithmId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
fn get_elem_len(self) -> usize {
|
fn get_elem_len(self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
AlgorithmId::Rsa1024 => 64,
|
AlgorithmId::Rsa1024 => 64,
|
||||||
@@ -400,6 +404,7 @@ impl AlgorithmId {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
fn get_param_tag(self) -> u8 {
|
fn get_param_tag(self) -> u8 {
|
||||||
match self {
|
match self {
|
||||||
AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => 0x01,
|
AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => 0x01,
|
||||||
@@ -453,8 +458,7 @@ impl Key {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generate key
|
/// Generate new key.
|
||||||
#[allow(clippy::cognitive_complexity)]
|
|
||||||
pub fn generate(
|
pub fn generate(
|
||||||
yubikey: &mut YubiKey,
|
yubikey: &mut YubiKey,
|
||||||
slot: SlotId,
|
slot: SlotId,
|
||||||
@@ -473,7 +477,7 @@ pub fn generate(
|
|||||||
const SZ_ROCA_BLOCK_ADMIN: &str = "was blocked due to an administrator 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.";
|
const SZ_ROCA_DEFAULT: &str = "was permitted by default, but is not recommended. The default behavior will change in a future Yubico release.";
|
||||||
|
|
||||||
let setting_roca: settings::ConfigValue;
|
let setting_roca: settings::SettingValue;
|
||||||
|
|
||||||
match algorithm {
|
match algorithm {
|
||||||
AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => {
|
AlgorithmId::Rsa1024 | AlgorithmId::Rsa2048 => {
|
||||||
@@ -481,17 +485,17 @@ pub fn generate(
|
|||||||
&& (yubikey.version.minor < 3
|
&& (yubikey.version.minor < 3
|
||||||
|| yubikey.version.minor == 3 && (yubikey.version.patch < 5))
|
|| yubikey.version.minor == 3 && (yubikey.version.patch < 5))
|
||||||
{
|
{
|
||||||
setting_roca = settings::ConfigValue::get(SZ_SETTING_ROCA, true);
|
setting_roca = settings::SettingValue::get(SZ_SETTING_ROCA, true);
|
||||||
|
|
||||||
let psz_msg = match setting_roca.source {
|
let psz_msg = match setting_roca.source {
|
||||||
settings::Source::User => {
|
settings::SettingSource::User => {
|
||||||
if setting_roca.value {
|
if setting_roca.value {
|
||||||
SZ_ROCA_ALLOW_USER
|
SZ_ROCA_ALLOW_USER
|
||||||
} else {
|
} else {
|
||||||
SZ_ROCA_BLOCK_USER
|
SZ_ROCA_BLOCK_USER
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
settings::Source::Admin => {
|
settings::SettingSource::Admin => {
|
||||||
if setting_roca.value {
|
if setting_roca.value {
|
||||||
SZ_ROCA_ALLOW_ADMIN
|
SZ_ROCA_ALLOW_ADMIN
|
||||||
} else {
|
} else {
|
||||||
@@ -660,6 +664,7 @@ pub fn generate(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
fn write_key(
|
fn write_key(
|
||||||
yubikey: &mut YubiKey,
|
yubikey: &mut YubiKey,
|
||||||
slot: SlotId,
|
slot: SlotId,
|
||||||
@@ -708,6 +713,7 @@ fn write_key(
|
|||||||
|
|
||||||
/// The key data that makes up an RSA key.
|
/// The key data that makes up an RSA key.
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub struct RsaKeyData {
|
pub struct RsaKeyData {
|
||||||
/// The secret prime `p`.
|
/// The secret prime `p`.
|
||||||
p: Buffer,
|
p: Buffer,
|
||||||
@@ -769,6 +775,7 @@ impl RsaKeyData {
|
|||||||
///
|
///
|
||||||
/// Errors if `algorithm` isn't `AlgorithmId::Rsa1024` or `AlgorithmId::Rsa2048`.
|
/// Errors if `algorithm` isn't `AlgorithmId::Rsa1024` or `AlgorithmId::Rsa2048`.
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn import_rsa_key(
|
pub fn import_rsa_key(
|
||||||
yubikey: &mut YubiKey,
|
yubikey: &mut YubiKey,
|
||||||
slot: SlotId,
|
slot: SlotId,
|
||||||
@@ -803,6 +810,7 @@ pub fn import_rsa_key(
|
|||||||
///
|
///
|
||||||
/// Errors if `algorithm` isn't `AlgorithmId::EccP256` or ` AlgorithmId::EccP384`.
|
/// Errors if `algorithm` isn't `AlgorithmId::EccP256` or ` AlgorithmId::EccP384`.
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn import_ecc_key(
|
pub fn import_ecc_key(
|
||||||
yubikey: &mut YubiKey,
|
yubikey: &mut YubiKey,
|
||||||
slot: SlotId,
|
slot: SlotId,
|
||||||
@@ -828,8 +836,10 @@ pub fn import_ecc_key(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Generate an attestation certificate for a stored key.
|
/// Generate an attestation certificate for a stored key.
|
||||||
|
///
|
||||||
/// <https://developers.yubico.com/PIV/Introduction/PIV_attestation.html>
|
/// <https://developers.yubico.com/PIV/Introduction/PIV_attestation.html>
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn attest(yubikey: &mut YubiKey, key: SlotId) -> Result<Buffer> {
|
pub fn attest(yubikey: &mut YubiKey, key: SlotId) -> Result<Buffer> {
|
||||||
let templ = [0, Ins::Attest.code(), key.into(), 0];
|
let templ = [0, Ins::Attest.code(), key.into(), 0];
|
||||||
let txn = yubikey.begin_transaction()?;
|
let txn = yubikey.begin_transaction()?;
|
||||||
@@ -850,7 +860,7 @@ pub fn attest(yubikey: &mut YubiKey, key: SlotId) -> Result<Buffer> {
|
|||||||
Ok(Buffer::new(response.data().into()))
|
Ok(Buffer::new(response.data().into()))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sign data using a PIV key
|
/// Sign data using a PIV key.
|
||||||
pub fn sign_data(
|
pub fn sign_data(
|
||||||
yubikey: &mut YubiKey,
|
yubikey: &mut YubiKey,
|
||||||
raw_in: &[u8],
|
raw_in: &[u8],
|
||||||
@@ -863,8 +873,9 @@ pub fn sign_data(
|
|||||||
txn.authenticated_command(raw_in, algorithm, key, false)
|
txn.authenticated_command(raw_in, algorithm, key, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Decrypt data using a PIV key
|
/// Decrypt data using a PIV key.
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn decrypt_data(
|
pub fn decrypt_data(
|
||||||
yubikey: &mut YubiKey,
|
yubikey: &mut YubiKey,
|
||||||
input: &[u8],
|
input: &[u8],
|
||||||
|
|||||||
+25
-23
@@ -34,14 +34,6 @@
|
|||||||
//!
|
//!
|
||||||
//! NOTE: RSASSA-PSS signatures and RSA-OAEP encryption may be supportable (TBD)
|
//! NOTE: RSASSA-PSS signatures and RSA-OAEP encryption may be supportable (TBD)
|
||||||
//!
|
//!
|
||||||
//! ## Status
|
|
||||||
//!
|
|
||||||
//! This is a work-in-progress effort, and while much of the library-level
|
|
||||||
//! code from upstream [yubico-piv-tool] has been translated into Rust
|
|
||||||
//! presenting a safe interface, much of it is still untested.
|
|
||||||
//!
|
|
||||||
//! Please see the [project's README.md for a complete status][status].
|
|
||||||
//!
|
|
||||||
//! ## History
|
//! ## History
|
||||||
//!
|
//!
|
||||||
//! This library is a Rust translation of the [yubico-piv-tool] utility by
|
//! This library is a Rust translation of the [yubico-piv-tool] utility by
|
||||||
@@ -83,7 +75,6 @@
|
|||||||
//! [YubiKey NEO]: https://support.yubico.com/support/solutions/articles/15000006494-yubikey-neo
|
//! [YubiKey NEO]: https://support.yubico.com/support/solutions/articles/15000006494-yubikey-neo
|
||||||
//! [YubiKey 4]: https://support.yubico.com/support/solutions/articles/15000006486-yubikey-4
|
//! [YubiKey 4]: https://support.yubico.com/support/solutions/articles/15000006486-yubikey-4
|
||||||
//! [YubiKey 5]: https://www.yubico.com/products/yubikey-5-overview/
|
//! [YubiKey 5]: https://www.yubico.com/products/yubikey-5-overview/
|
||||||
//! [status]: https://github.com/iqlusioninc/yubikey.rs#status
|
|
||||||
//! [yubico-piv-tool]: https://github.com/Yubico/yubico-piv-tool/
|
//! [yubico-piv-tool]: https://github.com/Yubico/yubico-piv-tool/
|
||||||
//! [Corrode]: https://github.com/jameysharp/corrode
|
//! [Corrode]: https://github.com/jameysharp/corrode
|
||||||
//! [piv-tool-guide]: https://www.yubico.com/wp-content/uploads/2016/05/Yubico_PIV_Tool_Command_Line_Guide_en.pdf
|
//! [piv-tool-guide]: https://www.yubico.com/wp-content/uploads/2016/05/Yubico_PIV_Tool_Command_Line_Guide_en.pdf
|
||||||
@@ -121,6 +112,7 @@
|
|||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||||
#![doc(
|
#![doc(
|
||||||
html_logo_url = "https://raw.githubusercontent.com/iqlusioninc/yubikey.rs/main/img/logo.png",
|
html_logo_url = "https://raw.githubusercontent.com/iqlusioninc/yubikey.rs/main/img/logo.png",
|
||||||
html_root_url = "https://docs.rs/yubikey/0.4.0-pre"
|
html_root_url = "https://docs.rs/yubikey/0.4.0-pre"
|
||||||
@@ -129,34 +121,44 @@
|
|||||||
#![warn(missing_docs, rust_2018_idioms, trivial_casts, unused_qualifications)]
|
#![warn(missing_docs, rust_2018_idioms, trivial_casts, unused_qualifications)]
|
||||||
|
|
||||||
mod apdu;
|
mod apdu;
|
||||||
pub mod cccid;
|
mod cccid;
|
||||||
pub mod certificate;
|
pub mod certificate;
|
||||||
pub mod chuid;
|
mod chuid;
|
||||||
pub mod config;
|
mod config;
|
||||||
pub mod error;
|
mod error;
|
||||||
pub mod key;
|
pub mod key;
|
||||||
mod metadata;
|
mod metadata;
|
||||||
pub mod mgm;
|
mod mgm;
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
pub mod mscmap;
|
mod mscmap;
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
pub mod msroots;
|
mod msroots;
|
||||||
pub mod policy;
|
mod policy;
|
||||||
pub mod readers;
|
pub mod readers;
|
||||||
mod serialization;
|
mod serialization;
|
||||||
pub mod settings;
|
mod settings;
|
||||||
mod transaction;
|
mod transaction;
|
||||||
pub mod yubikey;
|
mod yubikey;
|
||||||
|
|
||||||
pub use self::{
|
pub use crate::{
|
||||||
|
cccid::{CardId, Ccc},
|
||||||
|
chuid::ChuId,
|
||||||
|
config::Config,
|
||||||
error::{Error, Result},
|
error::{Error, Result},
|
||||||
key::Key,
|
key::Key,
|
||||||
mgm::MgmKey,
|
mgm::{MgmKey, MgmType},
|
||||||
|
policy::{PinPolicy, TouchPolicy},
|
||||||
readers::Readers,
|
readers::Readers,
|
||||||
yubikey::{Serial, YubiKey},
|
settings::{SettingSource, SettingValue},
|
||||||
|
yubikey::{CachedPin, Serial, Version, YubiKey},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Object identifiers
|
#[cfg(feature = "untested")]
|
||||||
|
pub use crate::{mscmap::MsContainer, msroots::MsRoots};
|
||||||
|
|
||||||
|
pub use uuid::Uuid;
|
||||||
|
|
||||||
|
/// Object identifiers: handles to particular objects stored on a YubiKey.
|
||||||
pub type ObjectId = u32;
|
pub type ObjectId = u32;
|
||||||
|
|
||||||
/// Buffer type (self-zeroizing byte vector)
|
/// Buffer type (self-zeroizing byte vector)
|
||||||
|
|||||||
+6
-1
@@ -73,7 +73,7 @@ pub(crate) const DES_LEN_3DES: usize = DES_LEN_DES * 3;
|
|||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
const ITER_MGM_PBKDF2: u32 = 10000;
|
const ITER_MGM_PBKDF2: u32 = 10000;
|
||||||
|
|
||||||
/// Management Key (MGM) key types (manual/derived/protected)
|
/// Management Key (MGM) key types (manual/derived/protected).
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum MgmType {
|
pub enum MgmType {
|
||||||
/// Manual
|
/// Manual
|
||||||
@@ -132,6 +132,7 @@ impl MgmKey {
|
|||||||
|
|
||||||
/// Get derived management key (MGM)
|
/// Get derived management key (MGM)
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn get_derived(yubikey: &mut YubiKey, pin: &[u8]) -> Result<Self> {
|
pub fn get_derived(yubikey: &mut YubiKey, pin: &[u8]) -> Result<Self> {
|
||||||
let txn = yubikey.begin_transaction()?;
|
let txn = yubikey.begin_transaction()?;
|
||||||
|
|
||||||
@@ -157,6 +158,7 @@ impl MgmKey {
|
|||||||
|
|
||||||
/// Get protected management key (MGM)
|
/// Get protected management key (MGM)
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn get_protected(yubikey: &mut YubiKey) -> Result<Self> {
|
pub fn get_protected(yubikey: &mut YubiKey) -> Result<Self> {
|
||||||
let txn = yubikey.begin_transaction()?;
|
let txn = yubikey.begin_transaction()?;
|
||||||
|
|
||||||
@@ -187,6 +189,7 @@ impl MgmKey {
|
|||||||
///
|
///
|
||||||
/// This will wipe any metadata related to derived and PIN-protected management keys.
|
/// This will wipe any metadata related to derived and PIN-protected management keys.
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn set_default(yubikey: &mut YubiKey) -> Result<()> {
|
pub fn set_default(yubikey: &mut YubiKey) -> Result<()> {
|
||||||
MgmKey::default().set_manual(yubikey, false)
|
MgmKey::default().set_manual(yubikey, false)
|
||||||
}
|
}
|
||||||
@@ -198,6 +201,7 @@ impl MgmKey {
|
|||||||
///
|
///
|
||||||
/// This will wipe any metadata related to derived and PIN-protected management keys.
|
/// This will wipe any metadata related to derived and PIN-protected management keys.
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn set_manual(&self, yubikey: &mut YubiKey, require_touch: bool) -> Result<()> {
|
pub fn set_manual(&self, yubikey: &mut YubiKey, require_touch: bool) -> Result<()> {
|
||||||
let txn = yubikey.begin_transaction()?;
|
let txn = yubikey.begin_transaction()?;
|
||||||
|
|
||||||
@@ -257,6 +261,7 @@ impl MgmKey {
|
|||||||
///
|
///
|
||||||
/// This enables key management operations to be performed with access to the PIN.
|
/// This enables key management operations to be performed with access to the PIN.
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn set_protected(&self, yubikey: &mut YubiKey) -> Result<()> {
|
pub fn set_protected(&self, yubikey: &mut YubiKey) -> Result<()> {
|
||||||
let txn = yubikey.begin_transaction()?;
|
let txn = yubikey.begin_transaction()?;
|
||||||
|
|
||||||
|
|||||||
+44
-40
@@ -1,7 +1,4 @@
|
|||||||
//! MS Container Map Records
|
//! MS Container Map Records.
|
||||||
//!
|
|
||||||
//! These appear(?) to be defined in Microsoft's Smart Card Minidriver Specification:
|
|
||||||
//! <https://docs.microsoft.com/en-us/previous-versions/windows/hardware/design/dn631754(v=vs.85)>
|
|
||||||
|
|
||||||
// Adapted from yubico-piv-tool:
|
// Adapted from yubico-piv-tool:
|
||||||
// <https://github.com/Yubico/yubico-piv-tool/>
|
// <https://github.com/Yubico/yubico-piv-tool/>
|
||||||
@@ -37,46 +34,53 @@ use crate::{key::SlotId, serialization::*, Error, Result, YubiKey, CB_OBJ_MAX};
|
|||||||
use log::error;
|
use log::error;
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
|
|
||||||
/// Container name length
|
|
||||||
const CONTAINER_NAME_LEN: usize = 40;
|
|
||||||
|
|
||||||
/// Container record length: 27 = 80 + 1 + 1 + 2 + 1 + 1 + 1 + 20
|
|
||||||
const CONTAINER_REC_LEN: usize = (2 * CONTAINER_NAME_LEN) + 27;
|
|
||||||
|
|
||||||
const OBJ_MSCMAP: u32 = 0x005f_ff10;
|
const OBJ_MSCMAP: u32 = 0x005f_ff10;
|
||||||
|
|
||||||
const TAG_MSCMAP: u8 = 0x81;
|
const TAG_MSCMAP: u8 = 0x81;
|
||||||
|
|
||||||
/// MS Container Map(?) Records
|
/// MS Container Map records.
|
||||||
|
///
|
||||||
|
/// Defined in Microsoft's Smart Card Minidriver Specification:
|
||||||
|
/// <https://docs.microsoft.com/en-us/previous-versions/windows/hardware/design/dn631754(v=vs.85)>
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Container {
|
pub struct MsContainer {
|
||||||
/// Container name
|
/// Container name.
|
||||||
pub name: [u16; CONTAINER_NAME_LEN],
|
pub name: [u16; Self::NAME_LEN],
|
||||||
|
|
||||||
/// Card slot
|
/// Card slot.
|
||||||
pub slot: SlotId,
|
pub slot: SlotId,
|
||||||
|
|
||||||
/// Key spec
|
/// Key spec.
|
||||||
pub key_spec: u8,
|
pub key_spec: u8,
|
||||||
|
|
||||||
/// Key size in bits
|
/// Key size in bits.
|
||||||
pub key_size_bits: u16,
|
pub key_size_bits: u16,
|
||||||
|
|
||||||
/// Flags
|
/// Flags.
|
||||||
pub flags: u8,
|
pub flags: u8,
|
||||||
|
|
||||||
/// PIN ID
|
/// PIN ID.
|
||||||
pub pin_id: u8,
|
pub pin_id: u8,
|
||||||
|
|
||||||
/// Associated ECHD(?) container (typo of "ecdh" perhaps?)
|
/// Associated ECHD container.
|
||||||
pub associated_echd_container: u8,
|
pub associated_echd_container: u8,
|
||||||
|
|
||||||
/// Cert fingerprint
|
/// Cert fingerprint.
|
||||||
pub cert_fingerprint: [u8; 20],
|
pub cert_fingerprint: [u8; Self::CERT_FINGERPRINT_LEN],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Container {
|
impl MsContainer {
|
||||||
/// Read MS Container Map records
|
/// Container name length in UTF-16 chars.
|
||||||
|
const NAME_LEN: usize = 40;
|
||||||
|
|
||||||
|
/// Container record length: 27 = 80 + 1 + 1 + 2 + 1 + 1 + 1 + 20
|
||||||
|
const REC_LEN: usize = (2 * Self::NAME_LEN) + 27;
|
||||||
|
|
||||||
|
/// Length of a certificate fingerprint.
|
||||||
|
const CERT_FINGERPRINT_LEN: usize = 20;
|
||||||
|
|
||||||
|
/// Read MS Container Map records.
|
||||||
pub fn read_mscmap(yubikey: &mut YubiKey) -> Result<Vec<Self>> {
|
pub fn read_mscmap(yubikey: &mut YubiKey) -> Result<Vec<Self>> {
|
||||||
let txn = yubikey.begin_transaction()?;
|
let txn = yubikey.begin_transaction()?;
|
||||||
let response = txn.fetch_object(OBJ_MSCMAP)?;
|
let response = txn.fetch_object(OBJ_MSCMAP)?;
|
||||||
@@ -95,8 +99,8 @@ impl Container {
|
|||||||
return Err(Error::InvalidObject);
|
return Err(Error::InvalidObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
for chunk in tlv.value.chunks_exact(CONTAINER_REC_LEN) {
|
for chunk in tlv.value.chunks_exact(Self::REC_LEN) {
|
||||||
containers.push(Container::new(chunk)?);
|
containers.push(MsContainer::new(chunk)?);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(containers)
|
Ok(containers)
|
||||||
@@ -105,7 +109,7 @@ impl Container {
|
|||||||
/// Write MS Container Map records.
|
/// Write MS Container Map records.
|
||||||
pub fn write_mscmap(yubikey: &mut YubiKey, containers: &[Self]) -> Result<()> {
|
pub fn write_mscmap(yubikey: &mut YubiKey, containers: &[Self]) -> Result<()> {
|
||||||
let n_containers = containers.len();
|
let n_containers = containers.len();
|
||||||
let data_len = n_containers * CONTAINER_REC_LEN;
|
let data_len = n_containers * Self::REC_LEN;
|
||||||
|
|
||||||
let txn = yubikey.begin_transaction()?;
|
let txn = yubikey.begin_transaction()?;
|
||||||
|
|
||||||
@@ -115,7 +119,7 @@ impl Container {
|
|||||||
|
|
||||||
let mut buf = [0u8; CB_OBJ_MAX];
|
let mut buf = [0u8; CB_OBJ_MAX];
|
||||||
let offset = Tlv::write_as(&mut buf, TAG_MSCMAP, data_len, |buf| {
|
let offset = Tlv::write_as(&mut buf, TAG_MSCMAP, data_len, |buf| {
|
||||||
for (i, chunk) in buf.chunks_exact_mut(CONTAINER_REC_LEN).enumerate() {
|
for (i, chunk) in buf.chunks_exact_mut(Self::REC_LEN).enumerate() {
|
||||||
chunk.copy_from_slice(&containers[i].to_bytes());
|
chunk.copy_from_slice(&containers[i].to_bytes());
|
||||||
}
|
}
|
||||||
})?;
|
})?;
|
||||||
@@ -123,19 +127,19 @@ impl Container {
|
|||||||
txn.save_object(OBJ_MSCMAP, &buf[..offset])
|
txn.save_object(OBJ_MSCMAP, &buf[..offset])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse a container record from a byte slice
|
/// Parse a container record from a byte slice.
|
||||||
pub fn new(bytes: &[u8]) -> Result<Self> {
|
pub fn new(bytes: &[u8]) -> Result<Self> {
|
||||||
if bytes.len() != CONTAINER_REC_LEN {
|
if bytes.len() != Self::REC_LEN {
|
||||||
error!(
|
error!(
|
||||||
"couldn't parse PIV container: expected {}-bytes, got {}-bytes",
|
"couldn't parse PIV container: expected {}-bytes, got {}-bytes",
|
||||||
CONTAINER_REC_LEN,
|
Self::REC_LEN,
|
||||||
bytes.len()
|
bytes.len()
|
||||||
);
|
);
|
||||||
return Err(Error::ParseError);
|
return Err(Error::ParseError);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut name = [0u16; CONTAINER_NAME_LEN];
|
let mut name = [0u16; Self::NAME_LEN];
|
||||||
let name_bytes_len = CONTAINER_NAME_LEN * 2;
|
let name_bytes_len = Self::NAME_LEN * 2;
|
||||||
|
|
||||||
for (i, chunk) in bytes[..name_bytes_len].chunks_exact(2).enumerate() {
|
for (i, chunk) in bytes[..name_bytes_len].chunks_exact(2).enumerate() {
|
||||||
name[i] = u16::from_le_bytes(chunk.try_into().unwrap());
|
name[i] = u16::from_le_bytes(chunk.try_into().unwrap());
|
||||||
@@ -144,7 +148,7 @@ impl Container {
|
|||||||
let mut cert_fingerprint = [0u8; 20];
|
let mut cert_fingerprint = [0u8; 20];
|
||||||
cert_fingerprint.copy_from_slice(&bytes[(bytes.len() - 20)..]);
|
cert_fingerprint.copy_from_slice(&bytes[(bytes.len() - 20)..]);
|
||||||
|
|
||||||
Ok(Container {
|
Ok(Self {
|
||||||
name,
|
name,
|
||||||
slot: bytes[name_bytes_len].try_into()?,
|
slot: bytes[name_bytes_len].try_into()?,
|
||||||
key_spec: bytes[name_bytes_len + 1],
|
key_spec: bytes[name_bytes_len + 1],
|
||||||
@@ -160,17 +164,17 @@ impl Container {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse the container name as a UTF-16 string
|
/// Parse the container name as a UTF-16 string.
|
||||||
pub fn parse_name(&self) -> Result<String> {
|
pub fn parse_name(&self) -> Result<String> {
|
||||||
String::from_utf16(&self.name).map_err(|_| Error::ParseError)
|
String::from_utf16(&self.name).map_err(|_| Error::ParseError)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Serialize a container record as a byte size
|
/// Serialize a container record as a byte size.
|
||||||
pub fn to_bytes(&self) -> [u8; CONTAINER_REC_LEN] {
|
pub fn to_bytes(&self) -> [u8; Self::REC_LEN] {
|
||||||
// TODO(tarcieri): use array instead of `Vec`
|
// TODO(tarcieri): use array instead of `Vec`
|
||||||
let mut bytes = Vec::with_capacity(CONTAINER_REC_LEN);
|
let mut bytes = Vec::with_capacity(Self::REC_LEN);
|
||||||
|
|
||||||
for i in 0..CONTAINER_NAME_LEN {
|
for i in 0..Self::NAME_LEN {
|
||||||
bytes.extend_from_slice(&self.name[i].to_le_bytes());
|
bytes.extend_from_slice(&self.name[i].to_le_bytes());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +189,7 @@ impl Container {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TryFrom<&'a [u8]> for Container {
|
impl<'a> TryFrom<&'a [u8]> for MsContainer {
|
||||||
type Error = Error;
|
type Error = Error;
|
||||||
|
|
||||||
fn try_from(bytes: &'a [u8]) -> Result<Self> {
|
fn try_from(bytes: &'a [u8]) -> Result<Self> {
|
||||||
|
|||||||
+10
-9
@@ -1,11 +1,4 @@
|
|||||||
//! `msroots`: PKCS#7 formatted certificate store for enterprise trusted roots.
|
//! 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:
|
// Adapted from yubico-piv-tool:
|
||||||
// <https://github.com/Yubico/yubico-piv-tool/>
|
// <https://github.com/Yubico/yubico-piv-tool/>
|
||||||
@@ -53,7 +46,15 @@ const OBJ_MSROOTS5: u32 = 0x005f_ff15;
|
|||||||
const TAG_MSROOTS_END: u8 = 0x82;
|
const TAG_MSROOTS_END: u8 = 0x82;
|
||||||
const TAG_MSROOTS_MID: u8 = 0x83;
|
const TAG_MSROOTS_MID: u8 = 0x83;
|
||||||
|
|
||||||
/// `msroots` file: PKCS#7-formatted certificate store for enterprise trust roots
|
/// PKCS#7-formatted certificate store for enterprise trust roots.
|
||||||
|
///
|
||||||
|
/// The `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>
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub struct MsRoots(Vec<u8>);
|
pub struct MsRoots(Vec<u8>);
|
||||||
|
|
||||||
impl MsRoots {
|
impl MsRoots {
|
||||||
|
|||||||
+6
-4
@@ -3,8 +3,9 @@
|
|||||||
use crate::{serialization::Tlv, Result};
|
use crate::{serialization::Tlv, Result};
|
||||||
|
|
||||||
/// Specifies how often the PIN needs to be entered for access to the credential in a
|
/// Specifies how often the PIN needs to be entered for access to the credential in a
|
||||||
/// given slot. This policy must be set upon key generation or importation, and cannot be
|
/// given slot.
|
||||||
/// changed later.
|
///
|
||||||
|
/// This policy must be set when keys are generated or imported, and cannot be changed later.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum PinPolicy {
|
pub enum PinPolicy {
|
||||||
/// Use the default PIN policy for the slot. See the slot's documentation for details.
|
/// Use the default PIN policy for the slot. See the slot's documentation for details.
|
||||||
@@ -46,8 +47,9 @@ impl PinPolicy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Specifies under what conditions a physical touch on the metal contact is required, in
|
/// Specifies under what conditions a physical touch on the metal contact is required, in
|
||||||
/// addition to the [`PinPolicy`]. This policy must be set upon key generation or
|
/// addition to the [`PinPolicy`].
|
||||||
/// importation, and cannot be changed later.
|
///
|
||||||
|
/// This policy must be set when keys are generated or imported, and cannot be changed later.
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
pub enum TouchPolicy {
|
pub enum TouchPolicy {
|
||||||
/// Use the default touch policy for the slot.
|
/// Use the default touch policy for the slot.
|
||||||
|
|||||||
+7
-7
@@ -1,4 +1,4 @@
|
|||||||
//! Support for enumerating available readers
|
//! Support for enumerating available PC/SC card readers.
|
||||||
|
|
||||||
use crate::{Result, YubiKey};
|
use crate::{Result, YubiKey};
|
||||||
use std::{
|
use std::{
|
||||||
@@ -32,7 +32,7 @@ impl Readers {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Iterate over the available readers
|
/// Iterate over the available readers.
|
||||||
pub fn iter(&mut self) -> Result<Iter<'_>> {
|
pub fn iter(&mut self) -> Result<Iter<'_>> {
|
||||||
let Self { ctx, reader_names } = self;
|
let Self { ctx, reader_names } = self;
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ impl Readers {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An individual connected reader
|
/// An individual connected PC/SC card reader.
|
||||||
pub struct Reader<'ctx> {
|
pub struct Reader<'ctx> {
|
||||||
/// Name of this reader
|
/// Name of this reader
|
||||||
name: &'ctx CStr,
|
name: &'ctx CStr,
|
||||||
@@ -64,24 +64,24 @@ pub struct Reader<'ctx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'ctx> Reader<'ctx> {
|
impl<'ctx> Reader<'ctx> {
|
||||||
/// Create a new reader from its name and context
|
/// Create a new reader from its name and context.
|
||||||
fn new(name: &'ctx CStr, ctx: Arc<Mutex<pcsc::Context>>) -> Self {
|
fn new(name: &'ctx CStr, ctx: Arc<Mutex<pcsc::Context>>) -> Self {
|
||||||
// TODO(tarcieri): open devices, determine they're YubiKeys, get serial?
|
// TODO(tarcieri): open devices, determine they're YubiKeys, get serial?
|
||||||
Self { name, ctx }
|
Self { name, ctx }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get this reader's name
|
/// Get this reader's name.
|
||||||
pub fn name(&self) -> Cow<'_, str> {
|
pub fn name(&self) -> Cow<'_, str> {
|
||||||
// TODO(tarcieri): is lossy ok here? try to avoid lossiness?
|
// TODO(tarcieri): is lossy ok here? try to avoid lossiness?
|
||||||
self.name.to_string_lossy()
|
self.name.to_string_lossy()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Open a connection to this reader, returning a `YubiKey` if successful
|
/// Open a connection to this reader, returning a `YubiKey` if successful.
|
||||||
pub fn open(&self) -> Result<YubiKey> {
|
pub fn open(&self) -> Result<YubiKey> {
|
||||||
self.try_into()
|
self.try_into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Connect to this reader, returning its `pcsc::Card`
|
/// Connect to this reader, returning its `pcsc::Card`.
|
||||||
pub(crate) fn connect(&self) -> Result<pcsc::Card> {
|
pub(crate) fn connect(&self) -> Result<pcsc::Card> {
|
||||||
let ctx = self.ctx.lock().unwrap();
|
let ctx = self.ctx.lock().unwrap();
|
||||||
Ok(ctx.connect(self.name, pcsc::ShareMode::Shared, pcsc::Protocols::T1)?)
|
Ok(ctx.connect(self.name, pcsc::ShareMode::Shared, pcsc::Protocols::T1)?)
|
||||||
|
|||||||
+23
-18
@@ -40,43 +40,48 @@ use std::{
|
|||||||
io::{BufRead, BufReader},
|
io::{BufRead, BufReader},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Source of how a setting was configured
|
/// Source of how a setting was configured.
|
||||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
pub enum Source {
|
pub enum SettingSource {
|
||||||
/// User-specified setting
|
/// User-specified setting: sourced via `YUBIKEY_PIV_*` environment vars.
|
||||||
User,
|
User,
|
||||||
|
|
||||||
/// Admin-specified setting
|
/// Admin-specified setting: sourced via the `/etc/yubico/yubikeypiv.conf`
|
||||||
|
/// configuration file.
|
||||||
Admin,
|
Admin,
|
||||||
|
|
||||||
/// Default setting
|
/// Default setting.
|
||||||
Default,
|
Default,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Source {
|
impl Default for SettingSource {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Default
|
Self::Default
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Setting booleans
|
/// Setting booleans: configuration values sourced from a file or the environment.
|
||||||
|
///
|
||||||
|
/// These can be configured globally in `/etc/yubico/yubikeypiv.conf` by a
|
||||||
|
/// system administrator, or by the local user via `YUBIKEY_PIV_*` environment
|
||||||
|
/// variables.
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct ConfigValue {
|
pub struct SettingValue {
|
||||||
/// Boolean value
|
/// Boolean value
|
||||||
pub value: bool,
|
pub value: bool,
|
||||||
|
|
||||||
/// Source of the configuration setting (user, admin, or default)
|
/// Source of the configuration setting (user, admin, or default)
|
||||||
pub source: Source,
|
pub source: SettingSource,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConfigValue {
|
impl SettingValue {
|
||||||
/// Get a [`BoolValue`] value by name.
|
/// Get a [`SettingValue`] value by name.
|
||||||
pub fn get(key: &str, default: bool) -> Self {
|
pub fn get(key: &str, default: bool) -> Self {
|
||||||
Self::from_file(key)
|
Self::from_file(key)
|
||||||
.or_else(|| Self::from_env(key))
|
.or_else(|| Self::from_env(key))
|
||||||
.unwrap_or(Self {
|
.unwrap_or(Self {
|
||||||
value: default,
|
value: default,
|
||||||
source: Source::Default,
|
source: SettingSource::Default,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,8 +109,8 @@ impl ConfigValue {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if name == key {
|
if name == key {
|
||||||
return Some(ConfigValue {
|
return Some(SettingValue {
|
||||||
source: Source::Admin,
|
source: SettingSource::Admin,
|
||||||
value: value == "1" || value == "true",
|
value: value == "1" || value == "true",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -119,18 +124,18 @@ impl ConfigValue {
|
|||||||
fn from_env(key: &str) -> Option<Self> {
|
fn from_env(key: &str) -> Option<Self> {
|
||||||
env::var(format!("YUBIKEY_PIV_{}", key))
|
env::var(format!("YUBIKEY_PIV_{}", key))
|
||||||
.ok()
|
.ok()
|
||||||
.map(|value| ConfigValue {
|
.map(|value| SettingValue {
|
||||||
source: Source::User,
|
source: SettingSource::User,
|
||||||
value: value == "1" || value == "true",
|
value: value == "1" || value == "true",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ConfigValue {
|
impl Default for SettingValue {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
value: false,
|
value: false,
|
||||||
source: Source::default(),
|
source: SettingSource::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+46
-35
@@ -70,10 +70,10 @@ pub(crate) const KEY_CARDMGM: u8 = 0x9b;
|
|||||||
|
|
||||||
const TAG_DYN_AUTH: u8 = 0x7c;
|
const TAG_DYN_AUTH: u8 = 0x7c;
|
||||||
|
|
||||||
/// Cached YubiKey PIN
|
/// Cached YubiKey PIN.
|
||||||
pub type CachedPin = secrecy::SecretVec<u8>;
|
pub type CachedPin = secrecy::SecretVec<u8>;
|
||||||
|
|
||||||
/// YubiKey Serial Number
|
/// YubiKey serial number.
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||||
pub struct Serial(pub u32);
|
pub struct Serial(pub u32);
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ impl Display for Serial {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// YubiKey Version
|
/// YubiKey version.
|
||||||
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct Version {
|
pub struct Version {
|
||||||
/// Major version component
|
/// Major version component
|
||||||
@@ -133,8 +133,7 @@ impl Display for Version {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// YubiKey Device: this is the primary API for opening a session and
|
/// YubiKey device: primary API for opening a session and performing various operations.
|
||||||
/// performing various operations.
|
|
||||||
///
|
///
|
||||||
/// Almost all functionality in this library will require an open session
|
/// Almost all functionality in this library will require an open session
|
||||||
/// with a YubiKey which is represented by this type.
|
/// with a YubiKey which is represented by this type.
|
||||||
@@ -203,8 +202,9 @@ impl YubiKey {
|
|||||||
Err(Error::NotFound)
|
Err(Error::NotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Reconnect to a YubiKey
|
/// Reconnect to a YubiKey.
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn reconnect(&mut self) -> Result<()> {
|
pub fn reconnect(&mut self) -> Result<()> {
|
||||||
info!("trying to reconnect to current reader");
|
info!("trying to reconnect to current reader");
|
||||||
|
|
||||||
@@ -235,7 +235,7 @@ impl YubiKey {
|
|||||||
Transaction::new(&mut self.card)
|
Transaction::new(&mut self.card)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the name of the associated PC/SC card reader
|
/// Get the name of the associated PC/SC card reader.
|
||||||
pub fn name(&self) -> &str {
|
pub fn name(&self) -> &str {
|
||||||
&self.name
|
&self.name
|
||||||
}
|
}
|
||||||
@@ -259,12 +259,12 @@ impl YubiKey {
|
|||||||
Config::get(self)
|
Config::get(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get CHUID
|
/// Get Cardholder Unique Identifier (CHUID).
|
||||||
pub fn chuid(&mut self) -> Result<ChuId> {
|
pub fn chuid(&mut self) -> Result<ChuId> {
|
||||||
ChuId::get(self)
|
ChuId::get(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get CCCID
|
/// Get Cardholder Capability Container (CCC) Identifier.
|
||||||
pub fn cccid(&mut self) -> Result<Ccc> {
|
pub fn cccid(&mut self) -> Result<Ccc> {
|
||||||
Ccc::get(self)
|
Ccc::get(self)
|
||||||
}
|
}
|
||||||
@@ -325,6 +325,7 @@ impl YubiKey {
|
|||||||
|
|
||||||
/// Deauthenticate
|
/// Deauthenticate
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn deauthenticate(&mut self) -> Result<()> {
|
pub fn deauthenticate(&mut self) -> Result<()> {
|
||||||
let txn = self.begin_transaction()?;
|
let txn = self.begin_transaction()?;
|
||||||
|
|
||||||
@@ -359,7 +360,7 @@ impl YubiKey {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the number of PIN retries
|
/// Get the number of PIN retries.
|
||||||
pub fn get_pin_retries(&mut self) -> Result<u8> {
|
pub fn get_pin_retries(&mut self) -> Result<u8> {
|
||||||
let txn = self.begin_transaction()?;
|
let txn = self.begin_transaction()?;
|
||||||
|
|
||||||
@@ -376,8 +377,9 @@ impl YubiKey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the number of PIN retries
|
/// Set the number of PIN retries.
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn set_pin_retries(&mut self, pin_tries: u8, puk_tries: u8) -> Result<()> {
|
pub fn set_pin_retries(&mut self, pin_tries: u8, puk_tries: u8) -> Result<()> {
|
||||||
// Special case: if either retry count is 0, it's a successful no-op
|
// Special case: if either retry count is 0, it's a successful no-op
|
||||||
if pin_tries == 0 || puk_tries == 0 {
|
if pin_tries == 0 || puk_tries == 0 {
|
||||||
@@ -400,8 +402,9 @@ impl YubiKey {
|
|||||||
|
|
||||||
/// Change the Personal Identification Number (PIN).
|
/// Change the Personal Identification Number (PIN).
|
||||||
///
|
///
|
||||||
/// The default PIN code is 123456
|
/// The default PIN code is `123456`.
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn change_pin(&mut self, current_pin: &[u8], new_pin: &[u8]) -> Result<()> {
|
pub fn change_pin(&mut self, current_pin: &[u8], new_pin: &[u8]) -> Result<()> {
|
||||||
{
|
{
|
||||||
let txn = self.begin_transaction()?;
|
let txn = self.begin_transaction()?;
|
||||||
@@ -415,8 +418,9 @@ impl YubiKey {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set PIN last changed
|
/// Set PIN last changed.
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn set_pin_last_changed(yubikey: &mut YubiKey) -> Result<()> {
|
pub fn set_pin_last_changed(yubikey: &mut YubiKey) -> Result<()> {
|
||||||
let txn = yubikey.begin_transaction()?;
|
let txn = yubikey.begin_transaction()?;
|
||||||
|
|
||||||
@@ -450,15 +454,17 @@ impl YubiKey {
|
|||||||
///
|
///
|
||||||
/// The PUK is part of the PIV standard that the YubiKey follows.
|
/// The PUK is part of the PIV standard that the YubiKey follows.
|
||||||
///
|
///
|
||||||
/// The default PUK code is 12345678.
|
/// The default PUK code is `12345678`.
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn change_puk(&mut self, current_puk: &[u8], new_puk: &[u8]) -> Result<()> {
|
pub fn change_puk(&mut self, current_puk: &[u8], new_puk: &[u8]) -> Result<()> {
|
||||||
let txn = self.begin_transaction()?;
|
let txn = self.begin_transaction()?;
|
||||||
txn.change_ref(ChangeRefAction::ChangePuk, current_puk, new_puk)
|
txn.change_ref(ChangeRefAction::ChangePuk, current_puk, new_puk)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Block PUK: permanently prevent the PIN from becoming unblocked
|
/// Block PUK: permanently prevent the PIN from becoming unblocked.
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn block_puk(yubikey: &mut YubiKey) -> Result<()> {
|
pub fn block_puk(yubikey: &mut YubiKey) -> Result<()> {
|
||||||
let mut puk = [0x30, 0x42, 0x41, 0x44, 0x46, 0x30, 0x30, 0x44];
|
let mut puk = [0x30, 0x42, 0x41, 0x44, 0x46, 0x30, 0x30, 0x44];
|
||||||
let mut tries_remaining: i32 = -1;
|
let mut tries_remaining: i32 = -1;
|
||||||
@@ -488,24 +494,23 @@ impl YubiKey {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Attempt to set the "PUK blocked" flag in admin data.
|
// Attempt to set the "PUK blocked" flag in admin data.
|
||||||
|
let mut admin_data = AdminData::read(&txn)
|
||||||
let mut admin_data = if let Ok(admin_data) = AdminData::read(&txn) {
|
.map(|data| {
|
||||||
if let Ok(item) = admin_data.get_item(TAG_ADMIN_FLAGS_1) {
|
if let Ok(item) = data.get_item(TAG_ADMIN_FLAGS_1) {
|
||||||
if item.len() == flags.len() {
|
if item.len() == flags.len() {
|
||||||
flags.copy_from_slice(item)
|
flags.copy_from_slice(item)
|
||||||
} else {
|
} else {
|
||||||
error!(
|
error!(
|
||||||
"admin flags exist, but are incorrect size: {} (expected {})",
|
"admin flags exist, but are incorrect size: {} (expected {})",
|
||||||
item.len(),
|
item.len(),
|
||||||
flags.len()
|
flags.len()
|
||||||
);
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
admin_data
|
data
|
||||||
} else {
|
})
|
||||||
AdminData::default()
|
.unwrap_or_default();
|
||||||
};
|
|
||||||
|
|
||||||
flags[0] |= ADMIN_FLAGS_1_PUK_BLOCKED;
|
flags[0] |= ADMIN_FLAGS_1_PUK_BLOCKED;
|
||||||
|
|
||||||
@@ -523,27 +528,31 @@ impl YubiKey {
|
|||||||
/// Unblock a Personal Identification Number (PIN) using a previously
|
/// Unblock a Personal Identification Number (PIN) using a previously
|
||||||
/// configured PIN Unblocking Key (PUK).
|
/// configured PIN Unblocking Key (PUK).
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn unblock_pin(&mut self, puk: &[u8], new_pin: &[u8]) -> Result<()> {
|
pub fn unblock_pin(&mut self, puk: &[u8], new_pin: &[u8]) -> Result<()> {
|
||||||
let txn = self.begin_transaction()?;
|
let txn = self.begin_transaction()?;
|
||||||
txn.change_ref(ChangeRefAction::UnblockPin, puk, new_pin)
|
txn.change_ref(ChangeRefAction::UnblockPin, puk, new_pin)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fetch an object from the YubiKey
|
/// Fetch an object from the YubiKey.
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn fetch_object(&mut self, object_id: ObjectId) -> Result<Buffer> {
|
pub fn fetch_object(&mut self, object_id: ObjectId) -> Result<Buffer> {
|
||||||
let txn = self.begin_transaction()?;
|
let txn = self.begin_transaction()?;
|
||||||
txn.fetch_object(object_id)
|
txn.fetch_object(object_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Save an object
|
/// Save an object.
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn save_object(&mut self, object_id: ObjectId, indata: &mut [u8]) -> Result<()> {
|
pub fn save_object(&mut self, object_id: ObjectId, indata: &mut [u8]) -> Result<()> {
|
||||||
let txn = self.begin_transaction()?;
|
let txn = self.begin_transaction()?;
|
||||||
txn.save_object(object_id, indata)
|
txn.save_object(object_id, indata)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an auth challenge
|
/// Get an auth challenge.
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn get_auth_challenge(&mut self) -> Result<[u8; 8]> {
|
pub fn get_auth_challenge(&mut self) -> Result<[u8; 8]> {
|
||||||
let txn = self.begin_transaction()?;
|
let txn = self.begin_transaction()?;
|
||||||
|
|
||||||
@@ -559,8 +568,9 @@ impl YubiKey {
|
|||||||
Ok(response.data()[4..12].try_into().unwrap())
|
Ok(response.data()[4..12].try_into().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Verify an auth response
|
/// Verify an auth response.
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn verify_auth_response(&mut self, response: [u8; 8]) -> Result<()> {
|
pub fn verify_auth_response(&mut self, response: [u8; 8]) -> Result<()> {
|
||||||
let mut data = [0u8; 12];
|
let mut data = [0u8; 12];
|
||||||
data[0] = 0x7c;
|
data[0] = 0x7c;
|
||||||
@@ -591,6 +601,7 @@ impl YubiKey {
|
|||||||
///
|
///
|
||||||
/// The reset function is only available when both pins are blocked.
|
/// The reset function is only available when both pins are blocked.
|
||||||
#[cfg(feature = "untested")]
|
#[cfg(feature = "untested")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "untested")))]
|
||||||
pub fn reset_device(&mut self) -> Result<()> {
|
pub fn reset_device(&mut self) -> Result<()> {
|
||||||
let templ = [0, Ins::Reset.code(), 0, 0];
|
let templ = [0, Ins::Reset.code(), 0, 0];
|
||||||
let txn = self.begin_transaction()?;
|
let txn = self.begin_transaction()?;
|
||||||
|
|||||||
@@ -17,8 +17,7 @@ use x509::RelativeDistinguishedName;
|
|||||||
use yubikey::{
|
use yubikey::{
|
||||||
certificate::{Certificate, PublicKeyInfo},
|
certificate::{Certificate, PublicKeyInfo},
|
||||||
key::{self, AlgorithmId, Key, RetiredSlotId, SlotId},
|
key::{self, AlgorithmId, Key, RetiredSlotId, SlotId},
|
||||||
policy::{PinPolicy, TouchPolicy},
|
Error, MgmKey, PinPolicy, TouchPolicy, YubiKey,
|
||||||
Error, MgmKey, YubiKey,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
|||||||
Reference in New Issue
Block a user