Skip to content
Open
4 changes: 4 additions & 0 deletions src/apdu.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ pub enum Ins {
Verify = 0x20,
Select = 0xA4,
ReadBinary = 0xB0,
MseSet = 0x22,
GeneralAuth = 0x86,
}

impl From<u8> for Ins {
Expand All @@ -44,6 +46,8 @@ impl From<u8> for Ins {
0x20 => Self::Verify,
0xA4 => Self::Select,
0xB0 => Self::ReadBinary,
0x22 => Self::MseSet,
0x86 => Self::GeneralAuth,
_ => Self::Unknown,
}
}
Expand Down
10 changes: 10 additions & 0 deletions src/apdu/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,13 @@ pub fn read_data_group(data_group: DataGroup) -> Vec<APDUCommand> {
pub fn verify(data: impl Into<Vec<u8>>) -> APDUCommand {
APDUCommand::new(Ins::Verify, 0x80, 0x00, data.into(), Some(0x00))
}

/// Command to select the Restricted Identification protocol
pub fn set_at(data: impl Into<Vec<u8>>) -> APDUCommand {
APDUCommand::new(Ins::MseSet, 0x41, 0xA4, data.into(), Some(0x00))
}

/// Command to perform general authentication for Restricted Identification
pub fn general_auth(data: impl Into<Vec<u8>>) -> APDUCommand {
APDUCommand::new(Ins::GeneralAuth, 0x00, 0x00, data.into(), Some(0x00))
}
80 changes: 77 additions & 3 deletions src/apdu/utils.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use bincode::{Decode, Encode};
use rasn::types::ObjectIdentifier;
use rasn::types::{Integer, ObjectIdentifier as Oid};
use rasn::{AsnType, Encode as DerEncode, der::encode as der_encode};

use super::Result;
use crate::apdu::{self, APDUCommand, DataGroup, SecureMessaging};
use crate::asn1::cvcert::EcdsaPublicKey;
use crate::asn1::oid::{DATE_OF_BIRTH_OID, MUNICIPALITY_ID_OID};
use crate::asn1::utils::RestrictedIdAlg;
use crate::crypto::sym::Cipher;
use crate::cvcert::{AccessRight, AccessRights};

Expand Down Expand Up @@ -47,6 +50,8 @@ pub enum CmdType {
ReadBinary(DataGroup),
VerifyAge,
VerifyPlace,
SetAt,
GeneralAuth,
}

/// Protected APDU command that pairs the command with its decodable type
Expand All @@ -65,10 +70,18 @@ pub struct DecryptedAPDU {
pub is_success: bool,
}

/// Build protected APDU commands based on access rights
pub struct RestrictedIdParams {
pub alg: RestrictedIdAlg,
pub priv_key_ref: Integer,
pub pubkey_sector1: EcdsaPublicKey,
pub pubkey_sector2: Option<EcdsaPublicKey>,
}

/// Build protected APDU commands
pub fn build_protected_cmds(
access_rights: &AccessRights,
sm: &mut SecureMessaging,
restricted_id_params: Option<RestrictedIdParams>,
) -> Result<Vec<ProtectedAPDU>> {
let mut commands = vec![];

Expand Down Expand Up @@ -113,6 +126,11 @@ pub fn build_protected_cmds(
AccessRight::CommunityIdVerification => {
add_verify_cmds(&mut commands, sm, CmdType::VerifyPlace, MUNICIPALITY_ID_OID)?
}
AccessRight::RestrictedIdentification => {
if let Some(ref restricted_id_params) = restricted_id_params {
add_restricted_id_cmds(&mut commands, sm, restricted_id_params)?;
}
}
_ => {}
}
}
Expand Down Expand Up @@ -155,7 +173,7 @@ fn add_verify_cmds(
oid: &'static [u32],
) -> Result<()> {
sm.update_ssc();
let oid = ObjectIdentifier::new_unchecked(oid.into());
let oid = Oid::new_unchecked(oid.into());
let mut verify_cmd = apdu::verify(rasn::der::encode(&oid)?);
verify_cmd.cla = 0x8C;

Expand All @@ -167,3 +185,59 @@ fn add_verify_cmds(
sm.update_ssc();
Ok(())
}

#[derive(Debug, Clone, PartialEq, AsnType, DerEncode)]
#[rasn(tag(context, 0), delegate)]
struct RestrictedIdOid(Oid);

#[derive(Debug, Clone, PartialEq, AsnType, DerEncode)]
#[rasn(tag(context, 4), delegate)]
struct RestrictedIdKeyRef(Integer);

#[derive(Debug, Clone, AsnType, DerEncode)]
#[rasn(tag(application, 28))]
struct RestrictedIdSectorPubKey {
#[rasn(tag(context, 0))]
first_sector_pub_key: EcdsaPublicKey,
#[rasn(tag(context, 2))]
second_sector_pub_key: Option<EcdsaPublicKey>,
}

fn add_restricted_id_cmds(
commands: &mut Vec<ProtectedAPDU>,
sm: &mut SecureMessaging,
params: &RestrictedIdParams,
) -> Result<()> {
// First, select the Restricted Identification protocol
let mut set_at_data = Vec::with_capacity(24);
let oid = RestrictedIdOid(Oid::new_unchecked(params.alg.to_oid().into()));
let key_ref = RestrictedIdKeyRef(params.priv_key_ref.to_owned());
set_at_data.extend_from_slice(&der_encode(&oid)?);
set_at_data.extend_from_slice(&der_encode(&key_ref)?);

sm.update_ssc();
let set_at_cmd = apdu::set_at(&*set_at_data);
let secured_set_at = sm.create_secure_command(&set_at_cmd)?;
commands.push(ProtectedAPDU {
cmd: secured_set_at,
cmd_type: CmdType::SetAt,
});
sm.update_ssc();

// Then send sector public key
let sector_key = RestrictedIdSectorPubKey {
first_sector_pub_key: params.pubkey_sector1.to_owned(),
second_sector_pub_key: params.pubkey_sector2.to_owned(),
};
let sector_key_data = der_encode(&sector_key)?;

sm.update_ssc();
let general_auth_cmd = apdu::general_auth(&*sector_key_data);
let secured_general_auth = sm.create_secure_command(&general_auth_cmd)?;
commands.push(ProtectedAPDU {
cmd: secured_general_auth,
cmd_type: CmdType::GeneralAuth,
});
sm.update_ssc();
Ok(())
}
27 changes: 23 additions & 4 deletions src/asn1/cvcert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ pub struct Chat {

/// ECDSA Public Key
#[derive(Debug, Clone, Decode, Encode, AsnType)]
#[rasn(tag(application, 0x49))]
pub struct EcdsaPublicKey {
/// key object identifier
pub oid: Oid,
Expand Down Expand Up @@ -44,10 +43,29 @@ pub struct EcdsaPublicKey {

/// Certificate Extensions
#[derive(Debug, Clone, Decode, Encode, AsnType)]
#[rasn(tag(application, 0x05))]
#[rasn(delegate)]
#[rasn(tag(application, 0x05), delegate)]
pub struct CertificateExtensions(pub SequenceOf<Any>);

/// Terminal Sector public key certificate extension
#[derive(Debug, Clone, Decode, Encode, AsnType)]
#[rasn(tag(application, 0x13))]
pub struct TerminalSectorExt {
pub oid: Oid,
#[rasn(tag(context, 0))]
pub first_sector_hash: Option<OctetString>,
#[rasn(tag(context, 1))]
pub second_sector_hash: Option<OctetString>,
}

/// Certificate Description Extension
#[derive(Debug, Clone, Decode, Encode, AsnType)]
#[rasn(tag(application, 0x13))]
pub struct CertificateDescriptionExt {
pub oid: Oid,
#[rasn(tag(context, 0))]
pub description: OctetString,
}

/// Card Verifiable Certificate Body
#[derive(Debug, Clone, Decode, Encode, AsnType)]
#[rasn(tag(application, 0x4E))]
Expand All @@ -59,9 +77,10 @@ pub struct CvCertificateBody {
/// certificate authority reference
pub car: OctetString,
/// public key value and domain parameters
#[rasn(tag(application, 0x49))]
pub public_key: EcdsaPublicKey,
#[rasn(tag(application, 0x20))]
/// certificate holder reference
#[rasn(tag(application, 0x20))]
pub chr: OctetString,
/// certificate holder authorization template
pub chat: Chat,
Expand Down
10 changes: 10 additions & 0 deletions src/asn1/oid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,13 @@ pub const ID_MOBILE_EID_TYPE: &[u32] = &[0, 4, 0, 127, 0, 7, 3, 2, 3, 2];
pub const EID_TYPE_SE_CERTIFIED: &[u32] = &[0, 4, 0, 127, 0, 7, 3, 2, 3, 2, 1];
pub const EID_TYPE_SE_ENDORSED: &[u32] = &[0, 4, 0, 127, 0, 7, 3, 2, 3, 2, 2];
pub const EID_TYPE_HW_KEYSTORE: &[u32] = &[0, 4, 0, 127, 0, 7, 3, 2, 3, 2, 3];

// OIDs for Restricted Identification
pub const ID_RI_ECDH: &[u32] = &[0, 4, 0, 127, 0, 7, 2, 2, 5, 2];
pub const ID_RI_ECDH_SHA_256: &[u32] = &[0, 4, 0, 127, 0, 7, 2, 2, 5, 2, 3];
pub const ID_RI_ECDH_SHA_384: &[u32] = &[0, 4, 0, 127, 0, 7, 2, 2, 5, 2, 4];
pub const ID_RI_ECDH_SHA_512: &[u32] = &[0, 4, 0, 127, 0, 7, 2, 2, 5, 2, 5];
pub const ID_SECTOR_RI: &[u32] = &[0, 4, 0, 127, 0, 7, 3, 1, 3, 2];

// OIDs for certificate description extensions
pub const ID_DESCRIPTION: &[u32] = &[0, 4, 0, 127, 0, 7, 3, 1, 3, 1];
30 changes: 30 additions & 0 deletions src/asn1/security_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,33 @@ pub struct MobileEIDTypeInfo {
/// Version -- should be 1
pub version: Integer,
}

/// Restricted Identification Protocol Parameters
#[derive(Debug, Clone, PartialEq, Eq, Hash, AsnType, Encode, Decode)]
pub struct ProtocolParams {
/// Protocol version. Should be 1
pub version: Integer,
/// Indicate private key to be used
pub key_id: Integer,
/// Whether explicit authorization is required to use the corresponding secret key
pub authorized_only: bool,
}

/// Restricted Identification Info
#[derive(Debug, Clone, PartialEq, Eq, Hash, AsnType, Encode, Decode)]
pub struct RestrictedIdInfo {
/// Protocol OID
pub protocol: Oid,
/// Protocol parameters
pub params: ProtocolParams,
/// Could indicate maximum length of the supported sector specific public keys
pub max_key_len: Option<Integer>,
}

/// Restricted Identification Domain Parameter Info
#[derive(Debug, Clone, PartialEq, Eq, Hash, AsnType, Encode, Decode)]
pub struct RestrictedIdDomainParamInfo {
/// Protocol OID (id-RI-DH | id-RI-ECDH)
pub protocol: Oid,
pub domain_parameter: AlgorithmIdentifier,
}
Loading
Loading