Skip to content

Commit

Permalink
Merge pull request #60 from Nitrokey/manage-security-environment
Browse files Browse the repository at this point in the history
Manage security environment
  • Loading branch information
sosthene-nitrokey committed Oct 17, 2022
2 parents 2e55b4e + 57a1ced commit 4c57222
Show file tree
Hide file tree
Showing 6 changed files with 231 additions and 52 deletions.
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

0 comments on commit 4c57222

Please sign in to comment.