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

Manage security environment #60

Merged
merged 8 commits into from
Oct 17, 2022
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,14 @@ SPDX-License-Identifier: CC0-1.0

### Features

- Support using authentication keys for decryption and vice-versa with MANAGE SECURITY ENVIRONMENT ([#60][])
- Support PIN resets using a resetting code ([#63][])

### Bugfixes

- Fix the length of the Digital signature counter DO 0x93 ([#76][])

[#60]: https://github.com/Nitrokey/opcard-rs/pull/60
[#63]: https://github.com/Nitrokey/opcard-rs/pull/63
[#76]: https://github.com/Nitrokey/opcard-rs/pull/76

Expand Down
34 changes: 32 additions & 2 deletions src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@ mod gen;
mod private_key_template;
mod pso;

use hex_literal::hex;
use iso7816::Status;

use crate::card::{Context, LoadedContext, RID};
use crate::error::Error;
use crate::state::{
LifeCycle, State, MAX_GENERIC_LENGTH, MAX_PIN_LENGTH, MIN_LENGTH_ADMIN_PIN,
KeyRef, LifeCycle, State, MAX_GENERIC_LENGTH, MAX_PIN_LENGTH, MIN_LENGTH_ADMIN_PIN,
MIN_LENGTH_RESET_CODE, MIN_LENGTH_USER_PIN,
};
use crate::tlv;
Expand Down Expand Up @@ -82,6 +83,7 @@ impl Command {
Self::SelectData(occurrence) => select_data(context, *occurrence),
Self::GetChallenge(length) => get_challenge(context, *length),
Self::ResetRetryCounter(mode) => reset_retry_conter(context.load_state()?, *mode),
Self::ManageSecurityEnvironment(mode) => manage_security_environment(context, *mode),
_ => {
error!("Command not yet implemented: {:x?}", self);
Err(Status::FunctionNotSupported)
Expand Down Expand Up @@ -299,7 +301,7 @@ impl TryFrom<u8> for GenerateAsymmetricKeyPairMode {
}
}

#[derive(Debug, Eq, PartialEq)]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub enum ManageSecurityEnvironmentMode {
Authentication,
Dec,
Expand All @@ -321,6 +323,7 @@ impl TryFrom<u8> for ManageSecurityEnvironmentMode {
fn select<const R: usize, T: trussed::Client>(context: Context<'_, R, T>) -> Result<(), Status> {
if context.data.starts_with(&RID) {
context.state.runtime.cur_do = None;
context.state.runtime.keyrefs = Default::default();
Ok(())
} else {
info!("Selected application {:x?} not found", context.data);
Expand Down Expand Up @@ -630,3 +633,30 @@ fn get_challenge<const R: usize, T: trussed::Client>(

Ok(())
}

// § 7.2.18
fn manage_security_environment<const R: usize, T: trussed::Client>(
ctx: Context<'_, R, T>,
mode: ManageSecurityEnvironmentMode,
) -> Result<(), Status> {
let key_ref = match ctx.data {
hex!("83 01 02") => KeyRef::Dec,
hex!("83 01 03") => KeyRef::Aut,
_ => {
warn!(
"Manage Security Environment called with invalid reference: {:x?}",
ctx.data
);
return Err(Status::IncorrectDataParameter);
}
};
info!("MANAGE SECURITY ENVIRONMENT: mode = {mode:?}, ref = {key_ref:?}");

match mode {
ManageSecurityEnvironmentMode::Dec => ctx.state.runtime.keyrefs.pso_decipher = key_ref,
ManageSecurityEnvironmentMode::Authentication => {
ctx.state.runtime.keyrefs.internal_aut = key_ref
}
}
Ok(())
}
169 changes: 121 additions & 48 deletions src/command/pso.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,25 +7,37 @@ use trussed::try_syscall;
use trussed::types::*;

use crate::card::LoadedContext;
use crate::state::KeyRef;
use crate::tlv::get_do;
use crate::types::*;

fn check_uif<const R: usize, T: trussed::Client>(
ctx: LoadedContext<'_, R, T>,
key: KeyType,
) -> Result<(), Status> {
if ctx.state.internal.uif(key).is_enabled()
&& !ctx
.backend
.confirm_user_present()
.map_err(|_| Status::UnspecifiedNonpersistentExecutionError)?
{
if ctx.state.internal.uif(key).is_enabled() {
prompt_uif(ctx)
} else {
Ok(())
}
}

fn prompt_uif<const R: usize, T: trussed::Client>(
ctx: LoadedContext<'_, R, T>,
) -> Result<(), Status> {
let success = ctx
.backend
.confirm_user_present()
.map_err(|_| Status::UnspecifiedNonpersistentExecutionError)?;
if !success {
warn!("User presence confirmation timed out");
// FIXME SecurityRelatedIssues (0x6600 is not available?)
return Err(Status::SecurityStatusNotSatisfied);
Err(Status::SecurityStatusNotSatisfied)
} else {
Ok(())
}
Ok(())
}

// § 7.2.10
pub fn sign<const R: usize, T: trussed::Client>(
mut ctx: LoadedContext<'_, R, T>,
Expand Down Expand Up @@ -78,68 +90,129 @@ fn sign_ec<const R: usize, T: trussed::Client>(
ctx.reply.expand(&signature)
}

pub fn int_aut_key_mecha_uif<const R: usize, T: trussed::Client>(
ctx: LoadedContext<'_, R, T>,
) -> Result<(KeyId, Mechanism, bool), Status> {
let (key_type, mechanism) = match ctx.state.runtime.keyrefs.internal_aut {
KeyRef::Aut => (
KeyType::Aut,
match ctx.state.internal.aut_alg() {
AuthenticationAlgorithm::EcDsaP256 => Mechanism::P256Prehashed,
AuthenticationAlgorithm::Ed255 => Mechanism::Ed255,

AuthenticationAlgorithm::Rsa2k | AuthenticationAlgorithm::Rsa4k => {
error!("RSA is not implemented");
return Err(Status::ConditionsOfUseNotSatisfied);
}
},
),
KeyRef::Dec => (
KeyType::Dec,
match ctx.state.internal.dec_alg() {
DecryptionAlgorithm::X255 => {
warn!("Attempt to authenticate with X25519 key");
return Err(Status::ConditionsOfUseNotSatisfied);
}
DecryptionAlgorithm::EcDhP256 => Mechanism::P256Prehashed,
DecryptionAlgorithm::Rsa2k | DecryptionAlgorithm::Rsa4k => {
error!("RSA is not implemented");
return Err(Status::ConditionsOfUseNotSatisfied);
}
},
),
};

if mechanism == Mechanism::P256Prehashed && ctx.data.len() != 32 {
warn!(
"Attempt to sign with P256 with data length != 32: {}",
ctx.data.len()
);
return Err(Status::ConditionsOfUseNotSatisfied);
}

Ok((
ctx.state.internal.key_id(key_type).ok_or_else(|| {
warn!("Attempt to INTERNAL AUTHENTICATE without a key set");
Status::KeyReferenceNotFound
})?,
mechanism,
ctx.state.internal.uif(key_type).is_enabled(),
))
}
// § 7.2.13
pub fn internal_authenticate<const R: usize, T: trussed::Client>(
mut ctx: LoadedContext<'_, R, T>,
) -> Result<(), Status> {
let key_id = ctx.state.internal.key_id(KeyType::Aut).ok_or_else(|| {
warn!("Attempt to authenticate without a key set");
Status::KeyReferenceNotFound
})?;
if !ctx.state.runtime.other_verified {
warn!("Attempt to sign without PW1 verified");
return Err(Status::SecurityStatusNotSatisfied);
}

check_uif(ctx.lend(), KeyType::Aut)?;

match ctx.state.internal.aut_alg() {
AuthenticationAlgorithm::Ed255 => sign_ec(ctx, key_id, Mechanism::Ed255),
AuthenticationAlgorithm::EcDsaP256 => {
if ctx.data.len() != 32 {
return Err(Status::ConditionsOfUseNotSatisfied);
}
sign_ec(ctx, key_id, Mechanism::P256Prehashed)
}
_ => {
error!("Unimplemented operation");
Err(Status::ConditionsOfUseNotSatisfied)
}
let (key_id, mechanism, uif) = int_aut_key_mecha_uif(ctx.lend())?;
if uif {
prompt_uif(ctx.lend())?;
}

sign_ec(ctx, key_id, mechanism)
}

pub fn decipher_key_mecha_uif<const R: usize, T: trussed::Client>(
ctx: LoadedContext<'_, R, T>,
) -> Result<(KeyId, Mechanism, bool), Status> {
let (key_type, mechanism) = match ctx.state.runtime.keyrefs.pso_decipher {
KeyRef::Dec => (
KeyType::Dec,
match ctx.state.internal.dec_alg() {
DecryptionAlgorithm::X255 => Mechanism::X255,
DecryptionAlgorithm::EcDhP256 => Mechanism::P256,
DecryptionAlgorithm::Rsa2k | DecryptionAlgorithm::Rsa4k => {
error!("RSA is not implemented");
return Err(Status::ConditionsOfUseNotSatisfied);
}
},
),
KeyRef::Aut => (
KeyType::Aut,
match ctx.state.internal.aut_alg() {
AuthenticationAlgorithm::EcDsaP256 => Mechanism::P256,
AuthenticationAlgorithm::Ed255 => {
warn!("Attempt to decipher with Ed255 key");
return Err(Status::ConditionsOfUseNotSatisfied);
}

AuthenticationAlgorithm::Rsa2k | AuthenticationAlgorithm::Rsa4k => {
error!("RSA is not implemented");
return Err(Status::ConditionsOfUseNotSatisfied);
}
},
),
};

Ok((
ctx.state.internal.key_id(key_type).ok_or_else(|| {
warn!("Attempt to decrypt without a key set");
Status::KeyReferenceNotFound
})?,
mechanism,
ctx.state.internal.uif(key_type).is_enabled(),
))
}

// § 7.2.11
pub fn decipher<const R: usize, T: trussed::Client>(
ctx: LoadedContext<'_, R, T>,
mut ctx: LoadedContext<'_, R, T>,
) -> Result<(), Status> {
let key_id = ctx.state.internal.key_id(KeyType::Dec).ok_or_else(|| {
warn!("Attempt to authenticat without a key set");
Status::KeyReferenceNotFound
})?;
if !ctx.state.runtime.other_verified {
warn!("Attempt to sign without PW1 verified");
return Err(Status::SecurityStatusNotSatisfied);
}

if ctx.state.internal.uif(KeyType::Dec).is_enabled()
&& !ctx
.backend
.confirm_user_present()
.map_err(|_| Status::UnspecifiedNonpersistentExecutionError)?
{
warn!("User presence confirmation timed out");
// FIXME SecurityRelatedIssues (0x6600 is not available?)
return Err(Status::SecurityStatusNotSatisfied);
let (key_id, mechanism, uif) = decipher_key_mecha_uif(ctx.lend())?;
if uif {
prompt_uif(ctx.lend())?;
}

match ctx.state.internal.dec_alg() {
DecryptionAlgorithm::X255 => decrypt_ec(ctx, key_id, Mechanism::X255),
DecryptionAlgorithm::EcDhP256 => decrypt_ec(ctx, key_id, Mechanism::P256),
_ => {
error!("Unimplemented operation");
Err(Status::ConditionsOfUseNotSatisfied)
}
}
decrypt_ec(ctx, key_id, mechanism)
}

fn decrypt_ec<const R: usize, T: trussed::Client>(
Expand Down
23 changes: 23 additions & 0 deletions src/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -709,12 +709,35 @@ impl Internal {
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum KeyRef {
Dec,
Aut,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct KeyRefs {
// We can't use `KeyType` because the Signing key cannot be reassigned
pub pso_decipher: KeyRef,
pub internal_aut: KeyRef,
}

impl Default for KeyRefs {
fn default() -> KeyRefs {
KeyRefs {
pso_decipher: KeyRef::Dec,
internal_aut: KeyRef::Aut,
}
}
}

#[derive(Debug, Default, Clone, PartialEq, Eq)]
pub struct Runtime {
pub sign_verified: bool,
pub other_verified: bool,
pub admin_verified: bool,
pub cur_do: Option<(Tag, Occurrence)>,
pub keyrefs: KeyRefs,
}

/// DOs that can store arbitrary data from the user
Expand Down
1 change: 0 additions & 1 deletion src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,6 @@ impl TryFrom<&[u8]> for AuthenticationAlgorithm {
}

#[derive(Clone, Debug, Copy)]
#[allow(unused)]
pub enum KeyType {
Sign,
Dec,
Expand Down
Loading