Skip to content

Commit

Permalink
Merge pull request #139 from Nitrokey/data-encryption
Browse files Browse the repository at this point in the history
Complete request for data encryption
  • Loading branch information
sosthene-nitrokey committed Apr 13, 2023
2 parents e569230 + f2a35b8 commit 6c2cb25
Show file tree
Hide file tree
Showing 11 changed files with 836 additions and 278 deletions.
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ trussed = "0.1.0"
trussed-rsa-alloc = { version = "0.1.0", optional = true }
serde_repr = "0.1"
hex-literal = "0.3.4"
trussed-auth = "0.1.0"
trussed-auth = "0.2.1"

# optional dependencies
apdu-dispatch = { version = "0.1", optional = true }
Expand Down Expand Up @@ -85,8 +85,8 @@ log-error = []
interchange = { git = "https://github.com/trussed-dev/interchange", rev = "fe5633466640e1e9a8c06d9b5dd1d0af08c272af" }
littlefs2 = { git = "https://github.com/Nitrokey/littlefs2", tag = "v0.3.2-nitrokey-2" }
p256-cortex-m4 = { git = "https://github.com/Nitrokey/p256-cortex-m4", tag = "v0.1.0-alpha.6-nitrokey-1" }
trussed = { git = "https://github.com/Nitrokey/trussed" , tag = "v0.1.0-nitrokey.8" }
trussed-auth = { git = "https://github.com/trussed-dev/trussed-auth.git", tag= "v0.1.0"}
trussed = { git = "https://github.com/Nitrokey/trussed" , tag = "v0.1.0-nitrokey.9" }
trussed-auth = { git = "https://github.com/trussed-dev/trussed-auth.git", tag= "v0.2.1"}
trussed-rsa-alloc = { git = "https://github.com/Nitrokey/trussed-rsa-backend", rev = "311d2366f99cc300b03d61e7f6a0a07abd3e8700" }

[package.metadata.docs.rs]
Expand Down
28 changes: 9 additions & 19 deletions src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ fn reset_retry_conter<const R: usize, T: trussed::Client + AuthClient>(
}

fn reset_retry_conter_with_p3<const R: usize, T: trussed::Client + AuthClient>(
ctx: LoadedContext<'_, R, T>,
mut ctx: LoadedContext<'_, R, T>,
) -> Result<(), Status> {
if ctx.data.len() < MIN_LENGTH_USER_PIN || ctx.data.len() > MAX_PIN_LENGTH {
warn!(
Expand All @@ -568,13 +568,7 @@ fn reset_retry_conter_with_p3<const R: usize, T: trussed::Client + AuthClient>(
}

ctx.state
.persistent
.set_pin(
ctx.backend.client_mut(),
ctx.options.storage,
ctx.data,
Password::Pw1,
)
.reset_user_code_with_pw3(ctx.backend.client_mut(), ctx.options.storage, ctx.data)
.map_err(|_err| {
error!("Failed to change PIN: {_err}");
Status::UnspecifiedNonpersistentExecutionError
Expand All @@ -601,7 +595,7 @@ fn reset_retry_conter_with_code<const R: usize, T: trussed::Client + AuthClient>
let res = ctx
.state
.check_pin(ctx.backend.client_mut(), old, Password::ResetCode);
match res {
let rc_key = match res {
Err(Error::InvalidPin) => {
return Err(Status::RemainingRetries(
ctx.state
Expand All @@ -613,26 +607,22 @@ fn reset_retry_conter_with_code<const R: usize, T: trussed::Client + AuthClient>
error!("Failed to check reset code: {_err:?}");
return Err(Status::UnspecifiedNonpersistentExecutionError);
}
Ok(_reset_kek) => {}
}
Ok(rc_key) => rc_key,
};

if new.len() > MAX_PIN_LENGTH || new.len() < MIN_LENGTH_USER_PIN {
warn!("Attempt to set resetting code with invalid length");
return Err(Status::IncorrectDataParameter);
}

ctx.state
.persistent
.set_pin(
ctx.backend.client_mut(),
ctx.options.storage,
new,
Password::Pw1,
)
.reset_user_code_with_rc(ctx.backend.client_mut(), ctx.options.storage, new, rc_key)
.map_err(|_err| {
error!("Failed to change PIN: {_err:?}");
Status::UnspecifiedNonpersistentExecutionError
})
})?;
syscall!(ctx.backend.client_mut().delete(rc_key));
Ok(())
}

// § 7.2.5
Expand Down
142 changes: 120 additions & 22 deletions src/command/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ use hex_literal::hex;
use iso7816::Status;
use trussed::{
syscall, try_syscall,
types::{KeySerialization, Mechanism},
types::{KeyId, KeySerialization, Mechanism},
};
use trussed_auth::AuthClient;

const CHACHA_NONCE_SIZE: usize = 12;
const CHACHA_TAG_SIZE: usize = 16;

use crate::{
card::{Context, LoadedContext, Options},
command::{GetDataMode, Password, PutDataMode, Tag},
Expand Down Expand Up @@ -375,8 +378,12 @@ impl GetDataObject {
Self::KdfDo => get_arbitrary_do(context, ArbitraryDO::KdfDo)?,
Self::PrivateUse1 => get_arbitrary_do(context, ArbitraryDO::PrivateUse1)?,
Self::PrivateUse2 => get_arbitrary_do(context, ArbitraryDO::PrivateUse2)?,
Self::PrivateUse3 => get_arbitrary_do(context, ArbitraryDO::PrivateUse3)?,
Self::PrivateUse4 => get_arbitrary_do(context, ArbitraryDO::PrivateUse4)?,
Self::PrivateUse3 => {
get_arbitrary_user_enc_do(context.load_state()?, ArbitraryDO::PrivateUse3)?
}
Self::PrivateUse4 => {
get_arbitrary_admin_enc_do(context.load_state()?, ArbitraryDO::PrivateUse4)?
}
Self::CardHolderCertificate => cardholder_cert(context)?,
Self::SecureMessagingCertificate => return Err(Status::SecureMessagingNotSupported),
Self::CardHolderRelatedData
Expand Down Expand Up @@ -811,6 +818,54 @@ fn get_arbitrary_do<const R: usize, T: trussed::Client + AuthClient>(
ctx.reply.expand(&data)
}

fn get_arbitrary_enc_do<const R: usize, T: trussed::Client + AuthClient>(
mut ctx: LoadedContext<'_, R, T>,
obj: ArbitraryDO,
key: KeyId,
) -> Result<(), Status> {
let data = obj
.load(ctx.backend.client_mut(), ctx.options.storage)
.map_err(|_| Status::UnspecifiedNonpersistentExecutionError)?;
if data.is_empty() {
return Ok(());
}
let (data, tag) = data.split_at(data.len() - CHACHA_TAG_SIZE);
let (data, nonce) = data.split_at(data.len() - CHACHA_NONCE_SIZE);
let decrypted = syscall!(ctx.backend.client_mut().decrypt(
Mechanism::Chacha8Poly1305,
key,
data,
&[],
nonce,
tag
))
.plaintext
.ok_or(Status::UnspecifiedNonpersistentExecutionError)?;
ctx.reply.expand(&decrypted)
}

/// Get an arbitrary DO encrypted with the user key
fn get_arbitrary_user_enc_do<const R: usize, T: trussed::Client + AuthClient>(
ctx: LoadedContext<'_, R, T>,
obj: ArbitraryDO,
) -> Result<(), Status> {
let Some(k) = ctx.state.volatile.other_verified_kek() else {
return Err(Status::SecurityStatusNotSatisfied);
};
get_arbitrary_enc_do(ctx, obj, k)
}

/// Get an arbitrary DO encrypted with the admin key
fn get_arbitrary_admin_enc_do<const R: usize, T: trussed::Client + AuthClient>(
ctx: LoadedContext<'_, R, T>,
obj: ArbitraryDO,
) -> Result<(), Status> {
let Some(k) = ctx.state.volatile.admin_kek() else {
return Err(Status::SecurityStatusNotSatisfied);
};
get_arbitrary_enc_do(ctx, obj, k)
}

// § 7.2.8
pub fn put_data<const R: usize, T: trussed::Client + AuthClient>(
mut context: Context<'_, R, T>,
Expand Down Expand Up @@ -889,7 +944,7 @@ enum_subset! {
impl PutDataObject {
fn write_perm(&self) -> PermissionRequirement {
match self {
Self::PrivateUse2 | Self::PrivateUse4 => PermissionRequirement::User,
Self::PrivateUse1 | Self::PrivateUse3 => PermissionRequirement::User,
_ => PermissionRequirement::Admin,
}
}
Expand All @@ -901,8 +956,12 @@ impl PutDataObject {
match self {
Self::PrivateUse1 => put_arbitrary_do(ctx, ArbitraryDO::PrivateUse1)?,
Self::PrivateUse2 => put_arbitrary_do(ctx, ArbitraryDO::PrivateUse2)?,
Self::PrivateUse3 => put_arbitrary_do(ctx, ArbitraryDO::PrivateUse3)?,
Self::PrivateUse4 => put_arbitrary_do(ctx, ArbitraryDO::PrivateUse4)?,
Self::PrivateUse3 => {
put_arbitrary_user_enc_do(ctx.load_state()?, ArbitraryDO::PrivateUse3)?
}
Self::PrivateUse4 => {
put_arbitrary_admin_enc_do(ctx.load_state()?, ArbitraryDO::PrivateUse4)?
}
Self::LoginData => put_arbitrary_do(ctx, ArbitraryDO::LoginData)?,
Self::ExtendedHeaderList => {
super::private_key_template::put_private_key_template(ctx.load_state()?)?
Expand Down Expand Up @@ -954,7 +1013,7 @@ fn put_cardholder_cert<const R: usize, T: trussed::Client + AuthClient>(
const AES256_KEY_LEN: usize = 32;

fn put_enc_dec_key<const R: usize, T: trussed::Client + AuthClient>(
ctx: LoadedContext<'_, R, T>,
mut ctx: LoadedContext<'_, R, T>,
) -> Result<(), Status> {
if ctx.data.len() != AES256_KEY_LEN {
warn!(
Expand All @@ -976,23 +1035,18 @@ fn put_enc_dec_key<const R: usize, T: trussed::Client + AuthClient>(
})?
.key;

let old_key = ctx
.state
.persistent
.set_aes_key_id(Some(new_key), ctx.backend.client_mut(), ctx.options.storage)
ctx.state
.set_aes_key(new_key, ctx.backend.client_mut(), ctx.options.storage)
.map_err(|_err| {
error!("Failed to set new key: {_err:?}");
Status::UnspecifiedNonpersistentExecutionError
})?;
if let Some(old_key) = old_key {
syscall!(ctx.backend.client_mut().delete(old_key));
}

Ok(())
}

fn put_resetting_code<const R: usize, T: trussed::Client + AuthClient>(
ctx: LoadedContext<'_, R, T>,
mut ctx: LoadedContext<'_, R, T>,
) -> Result<(), Status> {
if ctx.data.is_empty() {
info!("Removing resetting code");
Expand All @@ -1014,13 +1068,7 @@ fn put_resetting_code<const R: usize, T: trussed::Client + AuthClient>(
}

ctx.state
.persistent
.set_pin(
ctx.backend.client_mut(),
ctx.options.storage,
ctx.data,
Password::ResetCode,
)
.set_reset_code(ctx.backend.client_mut(), ctx.options.storage, ctx.data)
.map_err(|_err| {
error!("Failed to change resetting code: {_err}");
Status::UnspecifiedNonpersistentExecutionError
Expand Down Expand Up @@ -1200,6 +1248,56 @@ fn put_alg_attributes_aut<const R: usize, T: trussed::Client + AuthClient>(
.map_err(|_| Status::UnspecifiedNonpersistentExecutionError)
}

fn put_arbitrary_admin_enc_do<const R: usize, T: trussed::Client + AuthClient>(
ctx: LoadedContext<'_, R, T>,
obj: ArbitraryDO,
) -> Result<(), Status> {
let Some(k) = ctx.state.volatile.admin_kek() else {
return Err(Status::SecurityStatusNotSatisfied);
};
put_arbitrary_enc_do(ctx, obj, k)
}
fn put_arbitrary_user_enc_do<const R: usize, T: trussed::Client + AuthClient>(
ctx: LoadedContext<'_, R, T>,
obj: ArbitraryDO,
) -> Result<(), Status> {
let Some(k) =ctx.state.volatile.other_verified_kek() else {
return Err(Status::SecurityStatusNotSatisfied);
};
put_arbitrary_enc_do(ctx, obj, k)
}

fn put_arbitrary_enc_do<const R: usize, T: trussed::Client + AuthClient>(
ctx: LoadedContext<'_, R, T>,
obj: ArbitraryDO,
key: KeyId,
) -> Result<(), Status> {
if ctx.data.len() + CHACHA_NONCE_SIZE + CHACHA_TAG_SIZE > MAX_GENERIC_LENGTH {
return Err(Status::WrongLength);
}
let mut encrypted = syscall!(ctx.backend.client_mut().encrypt(
Mechanism::Chacha8Poly1305,
key,
ctx.data,
&[],
None
));
encrypted
.ciphertext
.extend_from_slice(&encrypted.nonce)
.map_err(|_| Status::NotEnoughMemory)?;
encrypted
.ciphertext
.extend_from_slice(&encrypted.tag)
.map_err(|_| Status::NotEnoughMemory)?;
obj.save(
ctx.backend.client_mut(),
ctx.options.storage,
&encrypted.ciphertext,
)
.map_err(|_| Status::UnspecifiedPersistentExecutionError)
}

fn put_arbitrary_do<const R: usize, T: trussed::Client + AuthClient>(
ctx: Context<'_, R, T>,
obj: ArbitraryDO,
Expand Down
Loading

0 comments on commit 6c2cb25

Please sign in to comment.