Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tests: Initial connect test and docs #19

Merged
merged 1 commit into from
Nov 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "yubikey-piv"
version = "0.0.1" # Also update html_root_url in lib.rs when bumping this
description = """
Pure Rust host-side driver for the YubiKey Personal Identity Verification (PIV)
CCID application providing general-purpose public-key signing and encryption
application providing general-purpose public-key signing and encryption
with hardware-backed private keys for RSA (2048/1024) and ECC (P-256/P-384)
algorithms (e.g, PKCS#1v1.5, ECDSA)
"""
Expand All @@ -26,3 +26,6 @@ pcsc = "2"
sha-1 = "0.8"
subtle = "2"
zeroize = "1"

[dev-dependencies]
env_logger = "0.7"
68 changes: 52 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,32 +33,68 @@ One small problem, it's not done yet... 😫

But it might be close?

## Minimum Supported Rust Version

- Rust **1.39+**

## Security Warning

No security audits of this crate have ever been performed. Presently it is in
an experimental stage and may still contain high-severity issues.

USE AT YOUR OWN RISK!

## History

This library is a Rust translation of the [yubico-piv-tool][3] utility by
Yubico, which was originally written in C. It was mechanically translated
from C into Rust using [Corrode][4], and then subsequently heavily
refactored into safer, more idiomatic Rust§.
refactored into safer, more idiomatic Rust.

Note that while this project started as a fork of a [Yubico][5] project,
this fork is **NOT** an official Yubico project and is in no way supported or
endorsed by Yubico.

§ *NOTE*: This section is actually full of lies and notes aspirations/goals,
not history. That said, there's been a decent amount of work cleaning up the
mechanically translated code, and at ~5klocs it's not that much.

## Security Warning

No security audits of this crate have ever been performed, and it has not been
thoroughly assessed to ensure its operation is constant-time on common CPU
architectures.

USE AT YOUR OWN RISK!

## Requirements

- Rust 1.39+
## Testing

To run the full test suite, you'll need a connected YubiKey NEO/4/5 device in
the default state (i.e. default PIN/PUK).

Tests which run live against a YubiKey device are marked as `#[ignore]` by
default in order to pass when running in a CI environment. To run these
tests locally, invoke the following command:

```
cargo test -- --ignored
```

This crate makes extensive use of the `log` facade to provide detailed
information about what is happening. If you'd like to print this logging
information while running the tests, set the `RUST_LOG` environment variable
to a relevant loglevel (e.g. `error`, `warn`, `info`, `debug`, `trace`):

```
RUST_LOG=info cargo test -- --ignored
```

To trace every message sent to/from the card i.e. the raw
Application Protocol Data Unit (APDU) messages, use the `trace` log level:

```
running 1 test
[INFO yubikey_piv::yubikey] trying to connect to reader 'Yubico YubiKey OTP+FIDO+CCID'
[INFO yubikey_piv::yubikey] connected to 'Yubico YubiKey OTP+FIDO+CCID' successfully
[TRACE yubikey_piv::transaction] >>> [0, 164, 4, 0, 5, 160, 0, 0, 3, 8]
[TRACE yubikey_piv::transaction] <<< [97, 17, 79, 6, 0, 0, 16, 0, 1, 0, 121, 7, 79, 5, 160, 0, 0, 3, 8, 144, 0]
[TRACE yubikey_piv::transaction] >>> [0, 253, 0, 0, 0]
[TRACE yubikey_piv::transaction] <<< [5, 1, 2, 144, 0]
[TRACE yubikey_piv::transaction] >>> [0, 248, 0, 0, 0]
[TRACE yubikey_piv::transaction] <<< [0, 115, 0, 178, 144, 0]
test connect ... ok
```

APDU messages labeled `>>>` are being sent to the YubiKey's internal SmartCard,
and ones labeled `<<<` are the responses.

## Code of Conduct

Expand Down
93 changes: 54 additions & 39 deletions src/apdu.rs
Original file line number Diff line number Diff line change
@@ -1,43 +1,74 @@
//! Application Protocol Data Unit (APDU)

// 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::{error::Error, response::Response, transaction::Transaction, Buffer};
use std::fmt::{self, Debug};
use zeroize::{Zeroize, Zeroizing};

/// Size of a serialized APDU (5 byte header + 255 bytes data)
pub const APDU_SIZE: usize = 260;
/// Maximum amount of command data that can be included in an APDU
const APDU_DATA_MAX: usize = 0xFF;

/// Application Protocol Data Unit (APDU).
///
/// These messages are packets used to communicate with the YubiKey using the
/// Chip Card Interface Device (CCID) protocol.
#[derive(Clone)]
pub(crate) struct APDU {
/// Instruction class - indicates the type of command, e.g. interindustry or proprietary
/// Instruction class: indicates the type of command (e.g. inter-industry or proprietary)
cla: u8,

/// Instruction code - indicates the specific command, e.g. "write data"
/// Instruction code: indicates the specific command (e.g. "write data")
ins: u8,

/// Instruction parameter 1 for the command, e.g. offset into file at which to write the data
/// Instruction parameter 1 for the command (e.g. offset into file at which to write the data)
p1: u8,

/// Instruction parameter 2 for the command
p2: u8,

/// Length of command - encodes the number of bytes of command data to follow
lc: u8,

/// Command data
data: [u8; 255],
/// Command data to be sent (`lc` is calculated as `data.len()`)
data: Vec<u8>,
}

impl APDU {
/// Create a new APDU with the given instruction code
pub fn new(ins: u8) -> Self {
let mut apdu = Self::default();
apdu.ins = ins;
apdu
Self {
cla: 0,
ins,
p1: 0,
p2: 0,
data: vec![],
}
}

/// Set this APDU's class
Expand All @@ -63,19 +94,18 @@ impl APDU {
///
/// Panics if the byte slice is more than 255 bytes!
pub fn data(&mut self, bytes: impl AsRef<[u8]>) -> &mut Self {
assert_eq!(self.lc, 0, "APDU command already set!");
assert!(self.data.is_empty(), "APDU command already set!");

let bytes = bytes.as_ref();

assert!(
bytes.len() <= self.data.len(),
"APDU command data too large: {}-bytes",
bytes.len()
bytes.len() <= APDU_DATA_MAX,
"APDU command data too long: {} (max: {})",
bytes.len(),
APDU_DATA_MAX
);

self.lc = bytes.len() as u8;
self.data[..bytes.len()].copy_from_slice(bytes);

self.data.extend_from_slice(bytes);
self
}

Expand All @@ -87,14 +117,13 @@ impl APDU {

/// Consume this APDU and return a self-zeroizing buffer
pub fn to_bytes(&self) -> Buffer {
let mut bytes = Vec::with_capacity(APDU_SIZE);
let mut bytes = Vec::with_capacity(5 + self.data.len());
bytes.push(self.cla);
bytes.push(self.ins);
bytes.push(self.p1);
bytes.push(self.p2);
bytes.push(self.lc);
bytes.push(self.data.len() as u8);
bytes.extend_from_slice(self.data.as_ref());

Zeroizing::new(bytes)
}
}
Expand All @@ -108,25 +137,12 @@ impl Debug for APDU {
self.ins,
self.p1,
self.p2,
self.lc,
&self.data[..]
self.data.len(),
self.data.as_slice()
)
}
}

impl Default for APDU {
fn default() -> Self {
Self {
cla: 0,
ins: 0,
p1: 0,
p2: 0,
lc: 0,
data: [0u8; 255],
}
}
}

impl Drop for APDU {
fn drop(&mut self) {
self.zeroize();
Expand All @@ -139,7 +155,6 @@ impl Zeroize for APDU {
self.ins.zeroize();
self.p1.zeroize();
self.p2.zeroize();
self.lc.zeroize();
self.data.zeroize();
}
}
31 changes: 18 additions & 13 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
//! [YubiKey][1] PIV: [Personal Identity Verification][2] support for
//! [Yubico][3] devices using the Chip Card Interface Device ([CCID][4])
//! protocol.
//! [Yubico][3] devices using the Personal Computer/Smart Card ([PC/SC][4])
//! interface as provided by the [`pcsc` crate][5].
//!
//! **PIV** is a [NIST][5] standard for both *signing* and *encryption*
//! **PIV** is a [NIST][6] standard for both *signing* and *encryption*
//! using SmartCards and SmartCard-based hardware tokens like YubiKeys.
//!
//! This library natively implements the CCID protocol used to manage and
//! utilize PIV encryption and signing keys which can be generated, imported,
//! and stored on YubiKey devices.
//!
//! See [Yubico's guide to PIV-enabled YubiKeys][6] for more information
//! See [Yubico's guide to PIV-enabled YubiKeys][7] for more information
//! on which devices support PIV and the available functionality.
//!
//! Supported algorithms:
Expand All @@ -25,26 +25,31 @@
//! This library is a work-in-progress translation and is not yet usable.
//! Check back later for updates.
//!
//! ## Minimum Supported Rust Version
//!
//! Rust 1.39+
//!
//! ## History
//!
//! This library is a Rust translation of the [yubico-piv-tool][7] utility by
//! This library is a Rust translation of the [yubico-piv-tool][8] utility by
//! Yubico, which was originally written in C. It was mechanically translated
//! from C into Rust using [Corrode][8], and then subsequently heavily
//! from C into Rust using [Corrode][9], and then subsequently heavily
//! refactored into safer, more idiomatic Rust.
//!
//! For more information on `yubico-piv-tool` and background information on how
//! the YubiKey implementation of PIV works in general, see the
//! [Yubico PIV Tool Command Line Guide][9].
//! [Yubico PIV Tool Command Line Guide][10].
//!
//! [1]: https://www.yubico.com/products/yubikey-hardware/
//! [2]: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-73-4.pdf
//! [3]: https://www.yubico.com/
//! [4]: https://en.wikipedia.org/wiki/CCID_(protocol)
//! [5]: https://www.nist.gov/
//! [6]: https://developers.yubico.com/PIV/Introduction/YubiKey_and_PIV.html
//! [7]: https://github.com/Yubico/yubico-piv-tool/
//! [8]: https://github.com/jameysharp/corrode
//! [9]: https://www.yubico.com/wp-content/uploads/2016/05/Yubico_PIV_Tool_Command_Line_Guide_en.pdf
//! [4]: https://en.wikipedia.org/wiki/PC/SC
//! [5]: https://github.com/bluetech/pcsc-rust
//! [6]: https://www.nist.gov/
//! [7]: https://developers.yubico.com/PIV/Introduction/YubiKey_and_PIV.html
//! [8]: https://github.com/Yubico/yubico-piv-tool/
//! [9]: https://github.com/jameysharp/corrode
//! [10]: https://www.yubico.com/wp-content/uploads/2016/05/Yubico_PIV_Tool_Command_Line_Guide_en.pdf

// Adapted from yubico-piv-tool:
// <https://github.com/Yubico/yubico-piv-tool/>
Expand Down
Loading