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

Add support for PUT DATA, GET DATA and fix GENERAL AUTHENTICATE #5

Merged
merged 74 commits into from
Mar 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
5473ccc
Fix initial parsing of GENERAL AUTHENTICATE
sosthene-nitrokey Nov 9, 2022
3ab233b
Implement request for challenge
sosthene-nitrokey Nov 10, 2022
2e62982
Implemet GENERAL AUTHENTICATE get response support
sosthene-nitrokey Nov 10, 2022
124d334
Check that command algorithm corresponds with state
sosthene-nitrokey Nov 10, 2022
050da53
Add test for admin auth
sosthene-nitrokey Nov 10, 2022
d21a04d
Fix set management key to accept any length
sosthene-nitrokey Nov 15, 2022
38b7a5a
Add support for Aes key management in tests
sosthene-nitrokey Nov 15, 2022
af7340b
Set the management algorithm with the key
sosthene-nitrokey Nov 16, 2022
0e6dbe5
Add support for AES keys in SET MANAGEMENT KEY and test AES keys for …
sosthene-nitrokey Nov 21, 2022
c036f5c
Add basic pivy test
sosthene-nitrokey Nov 21, 2022
1c9a505
Fix reuse compliance
sosthene-nitrokey Nov 22, 2022
ec5e758
Add pivy to CI docker image
sosthene-nitrokey Dec 16, 2022
ce366b6
Use Nitrokey trussed tag
sosthene-nitrokey Dec 16, 2022
22a6011
Fix clippy warning
sosthene-nitrokey Dec 16, 2022
d7d1a17
Fix documentation compilation
sosthene-nitrokey Dec 16, 2022
bfd80a4
WIP: Implement key generation
sosthene-nitrokey Nov 22, 2022
dfe8964
Rename keys to match standard
sosthene-nitrokey Nov 22, 2022
6faea4b
Add part of generate asymmetric keypair
sosthene-nitrokey Nov 23, 2022
bd6b2e6
Import `Reply` struct from opcard
sosthene-nitrokey Nov 23, 2022
a385ed0
Add support for generate asymmetric keypair
sosthene-nitrokey Nov 23, 2022
88f82bd
Accept multiple general authenticate fields
sosthene-nitrokey Dec 1, 2022
37bcd13
Make the PIV authentication key mandatory
sosthene-nitrokey Dec 1, 2022
0d6580c
Add support for with PIV authentication key
sosthene-nitrokey Dec 1, 2022
ce28dcb
Add test for key generation
sosthene-nitrokey Dec 1, 2022
25bd7c1
Add Put DATA parsing support
sosthene-nitrokey Dec 2, 2022
22f4669
Reuse generic container approach from Opcard
sosthene-nitrokey Dec 2, 2022
f8030fc
Remove unused warning
sosthene-nitrokey Dec 5, 2022
b6c745f
Use hex! macro
sosthene-nitrokey Dec 5, 2022
43b1a99
Implement generic PUT/GET DATA
sosthene-nitrokey Dec 5, 2022
6bc3ca7
Return NotFound when data object in not avalaible
sosthene-nitrokey Dec 5, 2022
ddb3132
Verify access permissions for PUT/GET DATA
sosthene-nitrokey Dec 5, 2022
a7d9b21
Add key generation test
sosthene-nitrokey Dec 5, 2022
1704d55
Add basic opensc test
sosthene-nitrokey Dec 7, 2022
a43f971
Add support for witness authentication
sosthene-nitrokey Dec 7, 2022
f77c780
Add admin test with opensc
sosthene-nitrokey Dec 7, 2022
07bd2dc
Fix generation of RSA keys
sosthene-nitrokey Dec 7, 2022
15a34f3
Fix make check
sosthene-nitrokey Dec 7, 2022
bd02b3d
Add configurability to tests
sosthene-nitrokey Dec 7, 2022
1b3f578
Fix put data command parsing
sosthene-nitrokey Dec 8, 2022
0b847c9
Fix get data
sosthene-nitrokey Dec 8, 2022
632120c
Validate PIN authentication before signing
sosthene-nitrokey Dec 8, 2022
0bbd13e
Remove outdated comment
sosthene-nitrokey Dec 8, 2022
cbf42d2
Add command_response tests for PUT DATA
sosthene-nitrokey Dec 8, 2022
6f5a82a
Generalize AsymetricKeyReference
sosthene-nitrokey Dec 8, 2022
a1f7c70
Add support for RESET RETRY COUNTER
sosthene-nitrokey Dec 8, 2022
f536bfc
Rename padded_pin => pin
sosthene-nitrokey Dec 8, 2022
5180c6b
Support all keys in GENERAL AUTHENTICATE
sosthene-nitrokey Dec 9, 2022
6dfbbb7
Implement exponentiation
sosthene-nitrokey Dec 9, 2022
ba1fe71
Copy instead of using references
sosthene-nitrokey Dec 9, 2022
7dd8b75
Add pivy test for ECDH
sosthene-nitrokey Dec 9, 2022
630482d
Use expect instead of check in tests for accurate testing
sosthene-nitrokey Dec 9, 2022
bf17ce6
Run cargo fmt
sosthene-nitrokey Dec 13, 2022
74085d3
Remove dbg!
sosthene-nitrokey Dec 13, 2022
be9a69d
Fix GENERAL AUTHENTICATE for agreement mechanisms
sosthene-nitrokey Dec 14, 2022
17e470a
Fix Rsa serialization order
sosthene-nitrokey Jan 4, 2023
4ac7f29
Fix clippy warning
sosthene-nitrokey Jan 4, 2023
8d79178
Add usbip example runner
sosthene-nitrokey Jan 6, 2023
a2a5768
Comment out failing test
sosthene-nitrokey Jan 6, 2023
b6b2822
Make apdu-dispatch a required feature for usbip
sosthene-nitrokey Jan 16, 2023
901cc67
Fix key history object
sosthene-nitrokey Jan 16, 2023
94fd398
Fix compilation in no-std
sosthene-nitrokey Jan 23, 2023
cd62aae
Add RSA to supported algorithms
sosthene-nitrokey Jan 26, 2023
4abb354
Fix memory leak
sosthene-nitrokey Jan 26, 2023
a6c4197
Strip PKCS padding
sosthene-nitrokey Jan 26, 2023
34d4e37
Fix tests
sosthene-nitrokey Feb 8, 2023
b280f49
Fix warnings
sosthene-nitrokey Feb 8, 2023
181c685
Use test_log
sosthene-nitrokey Feb 8, 2023
bb821d0
Use upstream usbd-ccid
sosthene-nitrokey Feb 8, 2023
6f6854d
Rename runtime to volatile
sosthene-nitrokey Feb 8, 2023
16e3e24
Migrate to trussed-rsa-backend
sosthene-nitrokey Feb 14, 2023
7cca1f0
Rework GENERAL AUTHENTICATE implementation
sosthene-nitrokey Feb 14, 2023
db20174
makefile: rename check to lint
sosthene-nitrokey Feb 16, 2023
98a7123
Use rsa backend in tests
sosthene-nitrokey Feb 28, 2023
17d9565
Add pivy rsa test
sosthene-nitrokey Feb 28, 2023
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
7 changes: 7 additions & 0 deletions .reuse/dep5
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: piv-authenticator
Source: https://github.com/Nitrokey/piv-authenticator

Files: tests/default_admin_key
Copyright: 2022 Nitrokey GmbH
License: LGPL-3.0-only
33 changes: 29 additions & 4 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ documentation = "https://docs.rs/piv-authenticator"
name = "virtual"
required-features = ["virtual"]


[[example]]
name = "usbip"
required-features = ["apdu-dispatch"]

[dependencies]
apdu-dispatch = { version = "0.1", optional = true }
delog = { version = "0.1.5", optional = true }
Expand All @@ -23,11 +28,13 @@ hex-literal = "0.3"
interchange = "0.2.2"
iso7816 = "0.1"
serde = { version = "1", default-features = false, features = ["derive"] }
trussed = "0.1"
trussed = { version = "0.1" }
untrusted = "0.9"
vpicc = { version = "0.1.0", optional = true }
log = "0.4"
heapless-bytes = "0.3.0"
subtle = { version = "2", default-features = false }
trussed-rsa-alloc = { git = "https://github.com/Nitrokey/trussed-rsa-backend.git", rev = "e29b26ab3800217b7eb73ecde67134bbb3acb9da", features = ["raw"] }

[dev-dependencies]
littlefs2 = "0.3.2"
Expand All @@ -37,15 +44,25 @@ env_logger = "0.9"
serde = { version = "1", features = ["derive"] }
serde_cbor = { version = "0.11", features = ["std"] }
hex = "0.4"
test-log = "0.2"
test-log = "0.2.11"
ron = "0.8"
des = "0.8"
aes = "0.8.2"
stoppable_thread = "0.2.1"
expectrl = "0.6.0"

# Examples
trussed-usbip = { git = "https://github.com/trussed-dev/pc-usbip-runner", default-features = false, features = ["ccid"], rev = "d2957b6c24c2b0cafbbfacd6fecd62c80943630b"}
usbd-ccid = { version = "0.2.0", features = ["highspeed-usb"]}
rand = "0.8.5"

[features]
default = []
strict-pin = []
std = []
virtual = ["std", "vpicc","trussed/virt"]
virtual = ["std", "vpicc", "trussed-rsa-alloc/virt"]
pivy-tests = []
opensc-tests = []

log-all = []
log-none = []
Expand All @@ -55,4 +72,12 @@ log-warn = []
log-error = []

[patch.crates-io]
trussed = { git = "https://github.com/trussed-dev/trussed", rev = "28478f8abed11d78c51e6a6a32326821ed61957a"}
# trussed = { git = "https://github.com/Nitrokey/trussed", tag = "v0.1.0-nitrokey-4"}
trussed = { git = "https://github.com/trussed-dev/trussed", rev = "d9276a689d68ffeb4c5d9ac635b18232be172f45"}
# littlefs2 = { git = "https://github.com/Nitrokey/littlefs2", tag = "v0.3.2-nitrokey-1" }

[profile.dev.package.rsa]
opt-level = 2

[profile.dev.package.num-bigint-dig]
opt-level = 2
13 changes: 9 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,36 @@
.NOTPARALLEL:

export RUST_LOG ?= info,cargo_tarpaulin=off
TEST_FEATURES ?=virtual,pivy-tests,opensc-tests

.PHONY: build-cortex-m4
build-cortex-m4:
cargo build --target thumbv7em-none-eabi

.PHONY: test
test:
cargo test --features virtual
cargo test --features $(TEST_FEATURES)

.PHONY: check
check:
RUSTLFAGS='-Dwarnings' cargo check --all-features --all-targets

.PHONY: lint
lint:
cargo fmt --check
cargo check --all-targets --all-features
RUSTLFAGS='-Dwarnings' cargo check --all-features --all-targets
cargo clippy --all-targets --all-features -- -Dwarnings
RUSTDOCFLAGS='-Dwarnings' cargo doc --all-features
reuse lint

.PHONY: tarpaulin
tarpaulin:
cargo tarpaulin --features virtual -o Html -o Xml
cargo tarpaulin --features $(TEST_FEATURES) -o Html -o Xml

.PHONY: example
example:
cargo run --example virtual --features virtual

.PHONY: ci
ci: check tarpaulin
ci: lint tarpaulin

12 changes: 11 additions & 1 deletion ci/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@

FROM docker.io/rust:latest

RUN apt update && apt install --yes scdaemon libclang-dev llvm python3-pip vsmartcard-vpcd pkg-config nettle-dev libpcsclite-dev
RUN apt update && apt install --yes libpcsclite-dev \
&& wget https://github.com/arekinath/pivy/releases/download/v0.10.0/pivy-0.10.0-src.tar.gz \
&& tar xvf pivy-0.10.0-src.tar.gz \
&& cd pivy-0.10.0 \
&& make pivy-tool

FROM docker.io/rust:latest

RUN apt update && apt install --yes scdaemon libclang-dev llvm python3-pip vsmartcard-vpcd pkg-config nettle-dev libpcsclite-dev opensc

RUN python3 -m pip install reuse

Expand All @@ -14,6 +22,8 @@ RUN cargo search

ENV CARGO_HOME=/app/.cache/cargo

COPY --from=0 pivy-0.10.0/pivy-tool /bin/pivy-tool

WORKDIR /app

COPY entrypoint.sh /entrypoint.sh
Expand Down
54 changes: 54 additions & 0 deletions examples/usbip.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (C) 2022 Nitrokey GmbH
// SPDX-License-Identifier: CC0-1.0

use trussed::virt::{self, Ram, UserInterface};
use trussed::{ClientImplementation, Platform};

use piv_authenticator as piv;
use trussed_usbip::Syscall;

const MANUFACTURER: &str = "Nitrokey";
const PRODUCT: &str = "Nitrokey 3";
const VID: u16 = 0x20a0;
const PID: u16 = 0x42b2;

struct PivApp {
piv: piv::Authenticator<ClientImplementation<Syscall<virt::Platform<Ram>>>>,
}

impl trussed_usbip::Apps<ClientImplementation<Syscall<virt::Platform<Ram>>>, ()> for PivApp {
fn new(
make_client: impl Fn(&str) -> ClientImplementation<Syscall<virt::Platform<Ram>>>,
_data: (),
) -> Self {
PivApp {
piv: piv::Authenticator::new(make_client("piv")),
}
}

fn with_ccid_apps<T>(
&mut self,
f: impl FnOnce(&mut [&mut dyn apdu_dispatch::App<7609, 7609>]) -> T,
) -> T {
f(&mut [&mut self.piv])
}
}

fn main() {
env_logger::init();

let options = trussed_usbip::Options {
manufacturer: Some(MANUFACTURER.to_owned()),
product: Some(PRODUCT.to_owned()),
serial_number: Some("TEST".into()),
vid: VID,
pid: PID,
};
trussed_usbip::Runner::new(virt::Ram::default(), options)
.init_platform(move |platform| {
let ui: Box<dyn trussed::platform::UserInterface + Send + Sync> =
Box::new(UserInterface::new());
platform.user_interface().set_inner(ui);
})
.exec::<PivApp, _, _>(|_platform| {});
}
2 changes: 1 addition & 1 deletion examples/virtual.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
fn main() {
env_logger::init();

trussed::virt::with_ram_client("piv-authenticator", |client| {
trussed_rsa_alloc::virt::with_ram_client("piv-authenticator", |client| {
let card = piv_authenticator::Authenticator::new(client);
let mut virtual_card = piv_authenticator::vpicc::VirtualCard::new(card);
let vpicc = vpicc::connect().expect("failed to connect to vpicc");
Expand Down
83 changes: 60 additions & 23 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ use core::convert::{TryFrom, TryInto};
// use flexiber::Decodable;
use iso7816::{Instruction, Status};

use crate::container::{Container, KeyReference};

use crate::state::TouchPolicy;
pub use crate::{
container::{
self as containers, AttestKeyReference, AuthenticateKeyReference,
ChangeReferenceKeyReference, GenerateAsymmetricKeyReference, VerifyKeyReference,
self as containers, AsymmetricKeyReference, AttestKeyReference, AuthenticateKeyReference,
ChangeReferenceKeyReference, GenerateKeyReference, VerifyKeyReference,
},
piv_types, Pin, Puk,
};
Expand All @@ -30,7 +32,7 @@ pub enum YubicoPivExtension {
SetPinRetries,
Attest(AttestKeyReference),
GetSerial, // also used via 0x01
GetMetadata,
GetMetadata(KeyReference),
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
Expand All @@ -51,23 +53,23 @@ pub enum Command<'l> {
/// Change PIN or PUK
ChangeReference(ChangeReference),
/// If the PIN is blocked, reset it using the PUK
ResetPinRetries(ResetPinRetries),
ResetRetryCounter(ResetRetryCounter),
/// The most general purpose method, performing actual cryptographic operations
///
/// In particular, this can also decrypt or similar.
GeneralAuthenticate(GeneralAuthenticate),
/// Store a data object / container.
PutData(PutData),
GenerateAsymmetric(GenerateAsymmetricKeyReference),
PutData(PutData<'l>),
GenerateAsymmetric(GenerateKeyReference),

/* Yubico commands */
YkExtension(YubicoPivExtension),
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct GeneralAuthenticate {
algorithm: piv_types::Algorithms,
key_reference: AuthenticateKeyReference,
pub algorithm: piv_types::Algorithms,
pub key_reference: AuthenticateKeyReference,
}

impl<'l> Command<'l> {
Expand Down Expand Up @@ -111,8 +113,7 @@ impl TryFrom<&[u8]> for GetData {
if tagged_slice.tag() != flexiber::Tag::application(0x1C) {
return Err(Status::IncorrectDataParameter);
}
let container: containers::Container = containers::Tag::new(tagged_slice.as_bytes())
.try_into()
let container = containers::Container::try_from(tagged_slice.as_bytes())
.map_err(|_| Status::IncorrectDataParameter)?;

info!("request to GetData for container {:?}", container);
Expand Down Expand Up @@ -218,19 +219,19 @@ impl TryFrom<ChangeReferenceArguments<'_>> for ChangeReference {
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct ResetPinRetries {
pub padded_pin: [u8; 8],
pub struct ResetRetryCounter {
pub pin: [u8; 8],
pub puk: [u8; 8],
}

impl TryFrom<&[u8]> for ResetPinRetries {
impl TryFrom<&[u8]> for ResetRetryCounter {
type Error = Status;
fn try_from(data: &[u8]) -> Result<Self, Self::Error> {
if data.len() != 16 {
return Err(Status::IncorrectDataParameter);
}
Ok(Self {
padded_pin: data[..8].try_into().unwrap(),
pin: data[..8].try_into().unwrap(),
puk: data[8..].try_into().unwrap(),
})
}
Expand All @@ -246,12 +247,48 @@ pub struct AuthenticateArguments<'l> {
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct PutData {}
pub enum PutData<'data> {
DiscoveryObject(&'data [u8]),
BitGroupTemplate(&'data [u8]),
Any(Container, &'data [u8]),
}

impl TryFrom<&[u8]> for PutData {
impl<'data> TryFrom<&'data [u8]> for PutData<'data> {
type Error = Status;
fn try_from(_data: &[u8]) -> Result<Self, Self::Error> {
todo!();
fn try_from(data: &'data [u8]) -> Result<Self, Self::Error> {
use crate::tlv::take_do;
let (tag, inner, rem) = take_do(data).ok_or_else(|| {
warn!("Failed to parse PUT DATA: {:02x?}", data);
Status::IncorrectDataParameter
})?;
if matches!(tag, 0x7E | 0x7F61) && !rem.is_empty() {
warn!("Empty remainder expected, got: {:02x?}", rem);
}

let container: Container = match tag {
0x7E => return Ok(PutData::DiscoveryObject(inner)),
0x7F61 => return Ok(PutData::BitGroupTemplate(inner)),
0x5C => Container::try_from(inner).map_err(|_| Status::IncorrectDataParameter)?,
_ => return Err(Status::IncorrectDataParameter),
};

let (tag, inner, rem) = take_do(rem).ok_or_else(|| {
warn!(
"Failed to parse PUT DATA's second field: {:02x?}, {:02x?}",
data, rem
);
Status::IncorrectDataParameter
})?;

if !rem.is_empty() {
warn!("Empty second remainder expected, got: {:02x?}", rem);
}

if tag != 0x53 {
warn!("Expected 0x53 tag, got: 0x{:02x?}", rem);
}

Ok(PutData::Any(container, inner))
}
}

Expand Down Expand Up @@ -310,7 +347,7 @@ impl<'l, const C: usize> TryFrom<&'l iso7816::Command<C>> for Command<'l> {
}

(0x00, Instruction::ResetRetryCounter, 0x00, 0x80) => {
Self::ResetPinRetries(ResetPinRetries::try_from(data.as_slice())?)
Self::ResetRetryCounter(ResetRetryCounter::try_from(data.as_slice())?)
}

(0x00, Instruction::GeneralAuthenticate, p1, p2) => {
Expand All @@ -327,7 +364,7 @@ impl<'l, const C: usize> TryFrom<&'l iso7816::Command<C>> for Command<'l> {
}

(0x00, Instruction::GenerateAsymmetricKeyPair, 0x00, p2) => {
Self::GenerateAsymmetric(GenerateAsymmetricKeyReference::try_from(p2)?)
Self::GenerateAsymmetric(GenerateKeyReference::try_from(p2)?)
}
// (0x00, 0x01, 0x10, 0x00)
(0x00, Instruction::Unknown(0x01), 0x00, 0x00) => {
Expand Down Expand Up @@ -359,9 +396,9 @@ impl<'l, const C: usize> TryFrom<&'l iso7816::Command<C>> for Command<'l> {
(0x00, Instruction::Unknown(0xf8), _, _) => {
Self::YkExtension(YubicoPivExtension::GetSerial)
}
(0x00, Instruction::Unknown(0xf7), _, _) => {
Self::YkExtension(YubicoPivExtension::GetMetadata)
}
(0x00, Instruction::Unknown(0xf7), 0x00, reference) => Self::YkExtension(
YubicoPivExtension::GetMetadata(KeyReference::try_from(reference)?),
),

_ => return Err(Status::FunctionNotSupported),
})
Expand Down
Loading