extern crate alloc;
use crate::delegate;
use crate::ts103097::extension_module::*;
use alloc::string::ToString;
use bon::Builder;
use rasn::error::InnerSubtypeConstraintError;
use rasn::prelude::*;
pub mod base_types;
pub mod crl_base_types;
use base_types::*;
pub const IEEE1609_DOT2_OID: &Oid = Oid::const_new(&[
1, 3, 111, 2, 1609, 2, 1, 1, 2, 6, ]);
pub const CERT_EXT_ID_OPERATING_ORGANIZATION: ExtId = ExtId(1);
pub const P2PCD8_BYTE_LEARNING_REQUEST_ID: ExtId = ExtId(1);
#[derive(Builder, AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(automatic_tags)]
pub struct Ieee1609Dot2Data {
#[builder(default = Ieee1609Dot2Data::VERSION)]
#[rasn(value("3"), identifier = "protocolVersion")]
pub protocol_version: Uint8,
pub content: Ieee1609Dot2Content,
}
impl Ieee1609Dot2Data {
pub const VERSION: u8 = 3;
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(choice, automatic_tags)]
#[non_exhaustive]
pub enum Ieee1609Dot2Content {
UnsecuredData(Opaque),
SignedData(alloc::boxed::Box<SignedData>),
EncryptedData(EncryptedData),
SignedCertificateRequest(Opaque),
#[rasn(extension_addition)]
SignedX509CertificateRequest(Opaque),
}
#[derive(Builder, AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(automatic_tags)]
pub struct SignedData {
#[rasn(identifier = "hashId")]
pub hash_id: HashAlgorithm,
#[rasn(identifier = "tbsData")]
pub tbs_data: ToBeSignedData,
pub signer: SignerIdentifier,
pub signature: Signature,
}
#[derive(Builder, AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(automatic_tags)]
pub struct ToBeSignedData {
pub payload: SignedDataPayload,
#[rasn(identifier = "headerInfo")]
pub header_info: HeaderInfo,
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(automatic_tags)]
#[non_exhaustive]
pub struct SignedDataPayload {
pub data: Option<Ieee1609Dot2Data>,
#[rasn(identifier = "extDataHash")]
pub ext_data_hash: Option<HashedData>,
#[rasn(extension_addition)]
pub omitted: Option<()>,
}
#[bon::bon]
impl SignedDataPayload {
#[builder]
pub fn new(
data: Option<Ieee1609Dot2Data>,
ext_data_hash: Option<HashedData>,
omitted: Option<()>,
) -> Result<Self, InnerSubtypeConstraintError> {
Self {
data,
ext_data_hash,
omitted,
}
.validate_components()
}
}
impl InnerSubtypeConstraint for SignedDataPayload {
fn validate_and_decode_containing(
self,
_: Option<rasn::Codec>,
) -> Result<Self, rasn::error::InnerSubtypeConstraintError> {
if self.data.is_none() && self.ext_data_hash.is_none() && self.omitted.is_none() {
return Err(InnerSubtypeConstraintError::MissingRequiredComponent {
component_path: "SignedDataPayload",
components: &["data", "ext_data_hash", "omitted"],
});
}
Ok(self)
}
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(choice, automatic_tags)]
#[non_exhaustive]
pub enum HashedData {
Sha256HashedData(HashedId32),
#[rasn(extension_addition)]
Sha384HashedData(HashedId48),
#[rasn(extension_addition)]
Sm3HashedData(HashedId32),
}
#[derive(Builder, AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(automatic_tags)]
#[non_exhaustive]
pub struct HeaderInfo {
pub psid: Psid,
#[rasn(identifier = "generationTime")]
pub generation_time: Option<Time64>,
#[rasn(identifier = "expiryTime")]
pub expiry_time: Option<Time64>,
#[rasn(identifier = "generationLocation")]
pub generation_location: Option<ThreeDLocation>,
#[rasn(identifier = "p2pcdLearningRequest")]
pub p2pcd_learning_request: Option<HashedId3>,
#[rasn(identifier = "missingCrlIdentifier")]
pub missing_crl_identifier: Option<MissingCrlIdentifier>,
#[rasn(identifier = "encryptionKey")]
pub encryption_key: Option<EncryptionKey>,
#[rasn(extension_addition, identifier = "inlineP2pcdRequest")]
pub inline_p2pcd_request: Option<SequenceOfHashedId3>,
#[rasn(extension_addition, identifier = "requestedCertificate")]
pub requested_certificate: Option<Certificate>,
#[rasn(extension_addition, identifier = "pduFunctionalType")]
pub pdu_functional_type: Option<PduFunctionalType>,
#[rasn(extension_addition, identifier = "contributedExtensions")]
pub contributed_extensions: Option<ContributedExtensionBlocks>,
}
#[derive(Builder, AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(automatic_tags)]
#[non_exhaustive]
pub struct MissingCrlIdentifier {
#[rasn(identifier = "cracaId")]
pub craca_id: HashedId3,
#[rasn(identifier = "crlSeries")]
pub crl_series: CrlSeries,
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate, value("0..=255"))]
pub struct PduFunctionalType(pub u8);
delegate!(u8, PduFunctionalType);
pub const TLS_HANDSHAKE: PduFunctionalType = PduFunctionalType(1);
pub const ISO21177_EXTENDED_AUTH: PduFunctionalType = PduFunctionalType(2);
pub const ISO21177_SESSION_EXTENSION: PduFunctionalType = PduFunctionalType(3);
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate, size("1.."))]
pub struct ContributedExtensionBlocks(pub SequenceOf<ContributedExtensionBlockType>);
delegate!(
SequenceOf<ContributedExtensionBlockType>,
ContributedExtensionBlocks
);
pub trait HeaderInfoContributedExtension {
type Extn: AsnType + Encode + Decode;
const ID: HeaderInfoContributorId;
}
#[derive(AsnType, Copy, Clone, Debug, Encode, Decode, PartialEq, Eq, Hash)]
#[rasn(enumerated)]
#[non_exhaustive]
pub enum Ieee1609HeaderInfoExtensions {}
impl ExtType for Ieee1609HeaderInfoExtensions {
type ExtContent = HashedId8;
const EXT_ID: ExtId = P2PCD8_BYTE_LEARNING_REQUEST_ID;
}
impl HeaderInfoContributedExtension for Ieee1609ContributedHeaderInfoExtension {
type Extn = Extension<Ieee1609HeaderInfoExtensions>;
const ID: HeaderInfoContributorId = IEEE1609_HEADER_INFO_CONTRIBUTOR_ID;
}
impl HeaderInfoContributedExtension for EtsiOriginatingHeaderInfoExtension {
type Extn = EtsiOriginatingHeaderInfoExtension;
const ID: HeaderInfoContributorId = ETSI_HEADER_INFO_CONTRIBUTOR_ID;
}
#[derive(AsnType, Debug, Encode, Decode, Clone, PartialEq, Eq, Hash)]
pub struct Ieee1609ContributedHeaderInfoExtension(pub Extension<Ieee1609HeaderInfoExtensions>);
delegate!(
Extension<Ieee1609HeaderInfoExtensions>,
Ieee1609ContributedHeaderInfoExtension
);
#[derive(AsnType, Debug, Encode, Decode, Clone, PartialEq, Eq, Hash)]
pub struct ContributedExtensionBlock<T: HeaderInfoContributedExtension> {
#[rasn(identifier = "contributorId")]
pub contributor_id: HeaderInfoContributorId,
#[rasn(size("1.."))]
pub extns: SequenceOf<T::Extn>,
}
#[derive(AsnType, Debug, Encode, Decode, Clone, PartialEq, Eq, Hash)]
#[rasn(choice)]
#[rasn(automatic_tags)]
#[non_exhaustive]
pub enum ContributedExtensionBlockType {
Ieee1609(ContributedExtensionBlock<Ieee1609ContributedHeaderInfoExtension>),
Etsi(ContributedExtensionBlock<EtsiOriginatingHeaderInfoExtension>),
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[rasn(delegate)]
pub struct HeaderInfoContributorId(pub u8);
delegate!(u8, HeaderInfoContributorId);
pub const ETSI_HEADER_INFO_CONTRIBUTOR_ID: HeaderInfoContributorId = HeaderInfoContributorId(2);
pub const IEEE1609_HEADER_INFO_CONTRIBUTOR_ID: HeaderInfoContributorId = HeaderInfoContributorId(1);
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(choice, automatic_tags)]
#[non_exhaustive]
pub enum SignerIdentifier {
Digest(HashedId8),
Certificate(SequenceOfCertificate),
#[rasn(identifier = "self")]
SelfSigned(()),
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate)]
pub struct Countersignature(Ieee1609Dot2Data);
#[bon::bon]
impl Countersignature {
#[builder]
fn new(data: Ieee1609Dot2Data) -> Result<Self, InnerSubtypeConstraintError> {
Self(data).validate_components()
}
}
impl InnerSubtypeConstraint for Countersignature {
fn validate_and_decode_containing(
self,
_: Option<rasn::Codec>,
) -> Result<Self, rasn::error::InnerSubtypeConstraintError> {
if let Ieee1609Dot2Content::SignedData(ref signed_data) = self.0.content {
if let SignedData {
tbs_data:
ToBeSignedData {
payload:
SignedDataPayload {
data: None,
ext_data_hash: Some(_),
..
},
header_info:
HeaderInfo {
generation_time: Some(_),
expiry_time: None,
generation_location: None,
p2pcd_learning_request: None,
missing_crl_identifier: None,
encryption_key: None,
..
},
..
},
..
} = **signed_data
{
Ok(self)
} else {
Err(InnerSubtypeConstraintError::InvalidComponentVariant {
component_path: "Ieee1609Dot2Data.content.tbsData",
component_type: "Ieee1609Dot2Content::SignedData",
details:
"SignedData does not match inner subtype constraint for Countersignature"
.to_string(),
})
}
} else {
Err(InnerSubtypeConstraintError::InvalidComponentVariant {
component_path: "Ieee1609Dot2Data.content",
component_type: "Ieee1609Dot2Content",
details: "SignedData variant is required for Countersignature".to_string(),
})
}
}
}
#[derive(Builder, AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(automatic_tags)]
pub struct EncryptedData {
pub recipients: SequenceOfRecipientInfo,
pub ciphertext: SymmetricCiphertext,
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(choice, automatic_tags)]
pub enum RecipientInfo {
PskRecipInfo(PreSharedKeyRecipientInfo),
SymmRecipInfo(SymmRecipientInfo),
CertRecipInfo(PKRecipientInfo),
SignedDataRecipInfo(PKRecipientInfo),
RekRecipInfo(PKRecipientInfo),
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate)]
pub struct SequenceOfRecipientInfo(pub SequenceOf<RecipientInfo>);
delegate!(SequenceOf<RecipientInfo>, SequenceOfRecipientInfo);
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate)]
pub struct PreSharedKeyRecipientInfo(pub HashedId8);
delegate!(HashedId8, PreSharedKeyRecipientInfo);
#[derive(Builder, AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(automatic_tags)]
pub struct SymmRecipientInfo {
#[rasn(identifier = "recipientId")]
pub recipient_id: HashedId8,
#[rasn(identifier = "encKey")]
pub enc_key: SymmetricCiphertext,
}
#[derive(Builder, AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(automatic_tags)]
pub struct PKRecipientInfo {
#[rasn(identifier = "recipientId")]
pub recipient_id: HashedId8,
#[rasn(identifier = "encKey")]
pub enc_key: EncryptedDataEncryptionKey,
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(choice, automatic_tags)]
#[non_exhaustive]
pub enum EncryptedDataEncryptionKey {
EciesNistP256(EciesP256EncryptedKey),
EciesBrainpoolP256r1(EciesP256EncryptedKey),
#[rasn(extension_addition)]
EcencSm2256(EcencP256EncryptedKey),
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(choice, automatic_tags)]
#[non_exhaustive]
pub enum SymmetricCiphertext {
Aes128ccm(One28BitCcmCiphertext),
#[rasn(extension_addition)]
Sm4Ccm(One28BitCcmCiphertext),
}
#[derive(Builder, AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(automatic_tags)]
pub struct One28BitCcmCiphertext {
pub nonce: FixedOctetString<12>,
#[rasn(identifier = "ccmCiphertext")]
pub ccm_ciphertext: Opaque,
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate)]
pub struct Aes128CcmCiphertext(pub One28BitCcmCiphertext);
delegate!(One28BitCcmCiphertext, Aes128CcmCiphertext);
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate)]
pub struct Certificate(CertificateBase);
impl From<ImplicitCertificate> for Certificate {
fn from(data: ImplicitCertificate) -> Self {
Self(data.0)
}
}
impl From<ExplicitCertificate> for Certificate {
fn from(data: ExplicitCertificate) -> Self {
Self(data.0)
}
}
impl core::ops::Deref for Certificate {
type Target = CertificateBase;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate)]
pub struct TestCertificate(pub Certificate);
delegate!(Certificate, TestCertificate);
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate)]
pub struct SequenceOfCertificate(pub SequenceOf<Certificate>);
delegate!(SequenceOf<Certificate>, SequenceOfCertificate);
#[derive(Builder, AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(automatic_tags)]
pub struct CertificateBase {
#[rasn(value("3"))]
#[builder(default = CertificateBase::VERSION)]
pub version: Uint8,
#[rasn(identifier = "type")]
pub r#type: CertificateType,
pub issuer: IssuerIdentifier,
#[rasn(identifier = "toBeSigned")]
pub to_be_signed: ToBeSignedCertificate,
pub signature: Option<Signature>,
}
impl CertificateBase {
pub const VERSION: u8 = 3;
#[must_use]
pub const fn is_implicit(&self) -> bool {
matches!(
&self,
CertificateBase {
r#type: CertificateType::Implicit,
to_be_signed: ToBeSignedCertificate {
verify_key_indicator: VerificationKeyIndicator::ReconstructionValue(_),
..
},
signature: None,
..
}
)
}
#[must_use]
pub const fn is_explicit(&self) -> bool {
matches!(
self,
CertificateBase {
r#type: CertificateType::Explicit,
to_be_signed: ToBeSignedCertificate {
verify_key_indicator: VerificationKeyIndicator::VerificationKey(_),
..
},
signature: Some(_),
..
}
)
}
}
#[derive(AsnType, Debug, Clone, Copy, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(enumerated)]
#[non_exhaustive]
pub enum CertificateType {
Explicit = 0,
Implicit = 1,
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate)]
pub struct ImplicitCertificate(CertificateBase);
impl ImplicitCertificate {
pub fn new(data: CertificateBase) -> Result<Self, InnerSubtypeConstraintError> {
Self(data).validate_components()
}
}
impl InnerSubtypeConstraint for ImplicitCertificate {
fn validate_and_decode_containing(
self,
_: Option<rasn::Codec>,
) -> Result<Self, rasn::error::InnerSubtypeConstraintError> {
if self.0.is_implicit() {
Ok(self)
} else {
Err(InnerSubtypeConstraintError::SubtypeConstraintViolation {
component_path: "ImplicitCertificate",
details: "CertificateBase is not implicit certificate",
})
}
}
}
impl core::ops::Deref for ImplicitCertificate {
type Target = CertificateBase;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate)]
pub struct ExplicitCertificate(CertificateBase);
impl ExplicitCertificate {
pub fn new(data: CertificateBase) -> Result<Self, InnerSubtypeConstraintError> {
Self(data).validate_components()
}
}
impl InnerSubtypeConstraint for ExplicitCertificate {
fn validate_and_decode_containing(
self,
_: Option<rasn::Codec>,
) -> Result<Self, rasn::error::InnerSubtypeConstraintError> {
if self.0.is_explicit() {
Ok(self)
} else {
Err(InnerSubtypeConstraintError::SubtypeConstraintViolation {
component_path: "ExplicitCertificate",
details: "CertificateBase is not explicit certificate",
})
}
}
}
impl core::ops::Deref for ExplicitCertificate {
type Target = CertificateBase;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(choice, automatic_tags)]
#[non_exhaustive]
pub enum IssuerIdentifier {
Sha256AndDigest(HashedId8),
#[rasn(identifier = "self")]
VSelf(HashAlgorithm),
#[rasn(extension_addition)]
Sha384AndDigest(HashedId8),
#[rasn(extension_addition)]
Sm3AndDigest(HashedId8),
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(automatic_tags)]
#[non_exhaustive]
pub struct ToBeSignedCertificate {
pub id: CertificateId,
#[rasn(identifier = "cracaId")]
pub craca_id: HashedId3,
#[rasn(identifier = "crlSeries")]
pub crl_series: CrlSeries,
#[rasn(identifier = "validityPeriod")]
pub validity_period: ValidityPeriod,
pub region: Option<GeographicRegion>,
#[rasn(identifier = "assuranceLevel")]
pub assurance_level: Option<SubjectAssurance>,
#[rasn(identifier = "appPermissions")]
pub app_permissions: Option<SequenceOfPsidSsp>,
#[rasn(identifier = "certIssuePermissions")]
pub cert_issue_permissions: Option<SequenceOfPsidGroupPermissions>,
#[rasn(identifier = "certRequestPermissions")]
pub cert_request_permissions: Option<SequenceOfPsidGroupPermissions>,
#[rasn(identifier = "canRequestRollover")]
pub can_request_rollover: Option<()>,
#[rasn(identifier = "encryptionKey")]
pub encryption_key: Option<PublicEncryptionKey>,
#[rasn(identifier = "verifyKeyIndicator")]
pub verify_key_indicator: VerificationKeyIndicator,
#[rasn(extension_addition)]
pub flags: Option<FixedBitString<8>>,
#[rasn(extension_addition, identifier = "appExtensions")]
pub app_extensions: Option<SequenceOfAppExtensions>,
#[rasn(extension_addition, identifier = "certIssueExtensions")]
pub cert_issue_extensions: Option<SequenceOfCertIssueExtensions>,
#[rasn(extension_addition, identifier = "certRequestExtension")]
pub cert_request_extension: Option<SequenceOfCertRequestExtensions>,
}
#[bon::bon]
impl ToBeSignedCertificate {
#[builder]
pub fn new(
id: CertificateId,
craca_id: HashedId3,
crl_series: CrlSeries,
validity_period: ValidityPeriod,
region: Option<GeographicRegion>,
assurance_level: Option<SubjectAssurance>,
app_permissions: Option<SequenceOfPsidSsp>,
cert_issue_permissions: Option<SequenceOfPsidGroupPermissions>,
cert_request_permissions: Option<SequenceOfPsidGroupPermissions>,
can_request_rollover: Option<()>,
encryption_key: Option<PublicEncryptionKey>,
verify_key_indicator: VerificationKeyIndicator,
flags: Option<FixedBitString<8>>,
app_extensions: Option<SequenceOfAppExtensions>,
cert_issue_extensions: Option<SequenceOfCertIssueExtensions>,
cert_request_extension: Option<SequenceOfCertRequestExtensions>,
) -> Result<Self, InnerSubtypeConstraintError> {
let cert = Self {
id,
craca_id,
crl_series,
validity_period,
region,
assurance_level,
app_permissions,
cert_issue_permissions,
cert_request_permissions,
can_request_rollover,
encryption_key,
verify_key_indicator,
flags,
app_extensions,
cert_issue_extensions,
cert_request_extension,
};
cert.validate_components()
}
}
impl InnerSubtypeConstraint for ToBeSignedCertificate {
fn validate_and_decode_containing(
self,
_: Option<rasn::Codec>,
) -> Result<Self, rasn::error::InnerSubtypeConstraintError> {
if self.app_permissions.is_none()
&& self.cert_issue_permissions.is_none()
&& self.cert_request_permissions.is_none()
{
return Err(InnerSubtypeConstraintError::MissingRequiredComponent {
component_path: "ToBeSignedCertificate",
components: &[
"app_permissions",
"cert_issue_permissions",
"cert_request_permissions",
],
});
}
Ok(self)
}
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(choice, automatic_tags)]
#[non_exhaustive]
pub enum CertificateId {
LinkageData(LinkageData),
Name(Hostname),
#[rasn(size("1..=64"))]
BinaryId(OctetString),
None(()),
}
#[derive(Builder, AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(automatic_tags)]
pub struct LinkageData {
#[rasn(identifier = "iCert")]
pub i_cert: IValue,
#[rasn(identifier = "linkage-value")]
pub linkage_value: LinkageValue,
#[rasn(identifier = "group-linkage-value")]
pub group_linkage_value: Option<GroupLinkageValue>,
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate, size(8))]
pub struct EndEntityType(pub FixedBitString<8usize>);
impl EndEntityType {
pub const APP: u8 = 0b1000_0000;
pub const ENROLL: u8 = 0b0100_0000;
pub const BOTH: u8 = 0b1100_0000;
}
delegate!(FixedBitString<8>, EndEntityType);
#[derive(Builder, AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(automatic_tags)]
pub struct PsidGroupPermissions {
#[rasn(identifier = "subjectPermissions")]
pub subject_permissions: SubjectPermissions,
#[rasn(
default = "psid_group_permissions_min_chain_length_default",
identifier = "minChainLength"
)]
#[builder(default = psid_group_permissions_min_chain_length_default())]
pub min_chain_length: Integer,
#[rasn(
default = "psid_group_permissions_chain_length_range_default",
identifier = "chainLengthRange"
)]
#[builder(default = psid_group_permissions_chain_length_range_default())]
pub chain_length_range: Integer,
#[rasn(
default = "psid_group_permissions_ee_type_default",
identifier = "eeType"
)]
#[builder(default = psid_group_permissions_ee_type_default())]
pub ee_type: EndEntityType,
}
fn psid_group_permissions_min_chain_length_default() -> Integer {
Integer::from(1)
}
fn psid_group_permissions_chain_length_range_default() -> Integer {
Integer::from(0)
}
fn psid_group_permissions_ee_type_default() -> EndEntityType {
EndEntityType(FixedBitString::new([
EndEntityType::APP,
0,
0,
0,
0,
0,
0,
0,
]))
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate)]
pub struct SequenceOfPsidGroupPermissions(pub SequenceOf<PsidGroupPermissions>);
delegate!(
SequenceOf<PsidGroupPermissions>,
SequenceOfPsidGroupPermissions
);
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(choice, automatic_tags)]
#[non_exhaustive]
pub enum SubjectPermissions {
Explicit(SequenceOfPsidSspRange),
All(()),
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(choice, automatic_tags)]
#[non_exhaustive]
pub enum VerificationKeyIndicator {
VerificationKey(PublicVerificationKey),
ReconstructionValue(EccP256CurvePoint),
}
#[derive(AsnType, Debug, Encode, Decode, Clone, PartialEq, Eq, Hash)]
#[rasn(automatic_tags)]
pub struct AppExtension<T: CertExtType> {
pub id: ExtId,
pub content: T::App,
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate, size("1.."))]
pub struct SequenceOfCertIssueExtensions(pub SequenceOf<OperatingOrganizationIssueExtension>);
delegate!(
SequenceOf<OperatingOrganizationIssueExtension>,
SequenceOfCertIssueExtensions
);
#[derive(AsnType, Debug, Encode, Decode, Clone, PartialEq, Eq, Hash)]
#[rasn(choice, automatic_tags)]
pub enum IssuePermissions<T: CertExtType> {
#[rasn(identifier = "specific")]
Specific(T::Issue),
#[rasn(identifier = "all")]
All(()),
}
#[derive(Builder, AsnType, Debug, Encode, Decode, Clone, PartialEq, Eq, Hash)]
#[rasn(automatic_tags)]
pub struct CertIssueExtension<T: CertExtType> {
pub id: ExtId,
pub permissions: IssuePermissions<T>,
}
impl<T: CertExtType> CertIssueExtension<T> {
pub fn new_specific(permissions: T::Issue) -> Self {
Self {
id: T::ID,
permissions: IssuePermissions::Specific(permissions),
}
}
pub fn new_all() -> Self {
Self {
id: T::ID,
permissions: IssuePermissions::All(()),
}
}
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate, size("1.."))]
pub struct SequenceOfCertRequestExtensions(pub SequenceOf<OperatingOrganizationRequestExtension>);
delegate!(
SequenceOf<OperatingOrganizationRequestExtension>,
SequenceOfCertRequestExtensions
);
#[derive(AsnType, Debug, Encode, Decode, Clone, PartialEq, Eq, Hash)]
#[rasn(choice, automatic_tags)]
pub enum RequestPermissions<T: CertExtType> {
#[rasn(identifier = "content")]
Content(T::Req),
#[rasn(identifier = "all")]
All(()),
}
#[derive(Builder, AsnType, Debug, Encode, Decode, Clone, PartialEq, Eq, Hash)]
#[rasn(automatic_tags)]
pub struct CertRequestExtension<T: CertExtType> {
pub id: ExtId,
pub permissions: RequestPermissions<T>,
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate)]
pub struct OperatingOrganizationId(pub ObjectIdentifier);
delegate!(ObjectIdentifier, OperatingOrganizationId);
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
pub struct OperatingOrganizationExtension;
impl CertExtType for OperatingOrganizationExtension {
type App = OperatingOrganizationId;
type Issue = (); type Req = (); const ID: ExtId = CERT_EXT_ID_OPERATING_ORGANIZATION;
}
pub type OperatingOrganizationAppExtension = AppExtension<OperatingOrganizationExtension>;
pub type OperatingOrganizationIssueExtension = CertIssueExtension<OperatingOrganizationExtension>;
pub type OperatingOrganizationRequestExtension =
CertRequestExtension<OperatingOrganizationExtension>;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum SetCertExtensionsType {
OperatingOrganization(OperatingOrganizationExtension),
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate, size("1.."))]
pub struct SequenceOfAppExtensions(pub SequenceOf<OperatingOrganizationAppExtension>);
delegate!(
SequenceOf<OperatingOrganizationAppExtension>,
SequenceOfAppExtensions
);
pub mod crl {
extern crate alloc;
use super::base_types::{Psid, Uint8};
use super::crl_base_types::CrlContents;
use super::{
CrlSeries, HeaderInfo, Ieee1609Dot2Content, Ieee1609Dot2Data, SignedData,
SignedDataPayload, ToBeSignedData,
};
use crate::delegate;
use bon::Builder;
use rasn::error::InnerSubtypeConstraintError;
use rasn::prelude::*;
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate, value("256"))]
pub struct CrlPsid(Psid);
impl CrlPsid {
pub const CRL_PSID: u16 = 256;
pub fn new() -> Self {
Self(Psid(Integer::from(Self::CRL_PSID)))
}
}
impl Default for CrlPsid {
fn default() -> Self {
Self::new()
}
}
delegate!(Psid, CrlPsid);
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate)]
pub struct SecuredCrl(Ieee1609Dot2Data);
impl InnerSubtypeConstraint for SecuredCrl {
fn validate_and_decode_containing(
self,
decode_containing_with: Option<rasn::Codec>,
) -> Result<Self, rasn::error::InnerSubtypeConstraintError> {
let signed_data_content;
if let Ieee1609Dot2Data {
content: Ieee1609Dot2Content::SignedData(signed_data),
..
} = &self.0
{
signed_data_content = &**signed_data;
} else {
return Err(InnerSubtypeConstraintError::MissingRequiredComponent {
component_path: "Ieee1609Dot2Data.content",
components: &["Ieee1609Dot2Content::SignedData"],
});
}
let crl_psid = CrlPsid::default();
if matches!(
signed_data_content,
SignedData {
tbs_data: ToBeSignedData {
payload: SignedDataPayload {
data: Some(Ieee1609Dot2Data {
protocol_version: _,
content: Ieee1609Dot2Content::UnsecuredData(_)
}),
..
},
header_info: HeaderInfo {
psid: _,
generation_time: None,
expiry_time: None,
generation_location: None,
p2pcd_learning_request: None,
missing_crl_identifier: None,
encryption_key: None,
..
},
},
..
}
) {
if let Some(codec) = decode_containing_with {
let inner_unsecured_data = signed_data_content
.tbs_data
.payload
.data
.as_ref()
.and_then(|data| {
if let Ieee1609Dot2Data {
content: Ieee1609Dot2Content::UnsecuredData(inner_data),
..
} = data
{
Some(inner_data)
} else {
None
}
});
if let Some(inner_unsecured_data) = inner_unsecured_data {
let decoded = codec.decode_from_binary::<CrlContents>(inner_unsecured_data);
match decoded {
Ok(_) => return Ok(self),
Err(err) => {
return Err(InnerSubtypeConstraintError::InvalidInnerContaining {
expected: "CrlContents",
err,
});
}
}
}
debug_assert!(false);
}
Ok(self)
} else if signed_data_content.tbs_data.header_info.psid == *crl_psid {
Err(InnerSubtypeConstraintError::InvalidComponentValue {
component_path: "Ieee1609Dot2Data.content.signedData.tbsData.headerInfo",
component_name: "psid",
details: alloc::format!(
"Expecting Psid value {} for SecuredCrl",
CrlPsid::CRL_PSID
),
})
} else {
Err(InnerSubtypeConstraintError::SubtypeConstraintViolation {
component_path: "Ieee1609Dot2Data.content.signedData",
details: "SignedData does not contain a valid CRL SPDU",
})
}
}
}
#[derive(Builder, AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq)]
#[rasn(automatic_tags)]
#[non_exhaustive]
pub struct CrlSsp {
#[rasn(value("1"))]
#[builder(default = CrlSsp::VERSION)]
#[rasn(identifier = "version")]
pub version: Uint8,
#[rasn(identifier = "associatedCraca")]
pub associated_craca: CracaType,
#[rasn(identifier = "crls")]
pub crls: PermissibleCrls,
}
impl CrlSsp {
pub const VERSION: u8 = 1;
}
#[derive(AsnType, Debug, Clone, Copy, Decode, Encode, PartialEq, Eq)]
#[rasn(enumerated)]
pub enum CracaType {
IsCraca,
IssuerIsCraca,
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate)]
pub struct PermissibleCrls(pub SequenceOf<CrlSeries>);
delegate!(SequenceOf<CrlSeries>, PermissibleCrls);
}
pub mod peer2peer {
use super::Certificate;
use super::base_types::Uint8;
use crate::delegate;
use bon::Builder;
use rasn::prelude::*;
#[derive(Builder, AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq)]
#[rasn(automatic_tags)]
#[non_exhaustive]
pub struct Ieee1609Dot2Peer2PeerPDU {
#[rasn(value("1"))]
#[builder(default = Ieee1609Dot2Peer2PeerPDU::VERSION)]
#[rasn(identifier = "version")]
pub version: Uint8,
#[rasn(identifier = "content")]
pub content: Ieee1609Dot2Peer2PeerPduContent,
}
impl Ieee1609Dot2Peer2PeerPDU {
pub const VERSION: u8 = 1;
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq)]
#[rasn(choice, automatic_tags)]
#[non_exhaustive]
pub enum Ieee1609Dot2Peer2PeerPduContent {
#[rasn(identifier = "caCerts")]
CaCerts(CaCertP2pPDU),
}
#[derive(AsnType, Debug, Clone, Decode, Encode, PartialEq, Eq, Hash)]
#[rasn(delegate)]
pub struct CaCertP2pPDU(pub SequenceOf<Certificate>);
delegate!(SequenceOf<Certificate>, CaCertP2pPDU);
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! round_trip {
($codec:ident, $typ:ty, $value:expr, $expected:expr) => {{
let value: $typ = $value;
let expected: &[u8] = $expected;
let actual_encoding = rasn::$codec::encode(&value).unwrap();
pretty_assertions::assert_eq!(&*actual_encoding, expected);
let decoded_value = rasn::$codec::decode::<$typ>(&actual_encoding);
match decoded_value {
Ok(decoded) => {
pretty_assertions::assert_eq!(value, decoded);
}
Err(err) => {
panic!("{:?}", err);
}
}
}};
}
#[test]
fn test_signed_data_payload() {
let payload_data = SignedDataPayload::builder()
.data(
Ieee1609Dot2Data::builder()
.protocol_version(3)
.content(Ieee1609Dot2Content::UnsecuredData(
OctetString::from("This is a BSM\r\n".as_bytes()).into(),
))
.build(),
)
.build()
.unwrap();
round_trip!(
coer,
SignedDataPayload,
payload_data,
&[
64, 3, 128, 15, 84, 104, 105, 115, 32, 105, 115, 32, 97, 32, 66, 83, 77, 13, 10
]
);
}
#[test]
fn test_ieee_basic_safety_message() {
let ieee1609dot2data: Ieee1609Dot2Data = Ieee1609Dot2Data::builder()
.protocol_version(3)
.content(Ieee1609Dot2Content::SignedData(Box::new(
SignedData::builder()
.hash_id(HashAlgorithm::Sha256)
.tbs_data(
ToBeSignedData::builder()
.payload(
SignedDataPayload::builder()
.data(
Ieee1609Dot2Data::builder()
.protocol_version(3)
.content(Ieee1609Dot2Content::UnsecuredData(
OctetString::from("This is a BSM\r\n".as_bytes())
.into(),
))
.build(),
)
.build()
.unwrap(),
)
.header_info(
HeaderInfo::builder()
.psid(Integer::from(32).into())
.generation_time(1_230_066_625_199_609_624.into())
.build(),
)
.build(),
)
.signer(SignerIdentifier::Digest(HashedId8(
"!\"#$%&'(".as_bytes().try_into().unwrap(),
)))
.signature(Signature::EcdsaNistP256(
EcdsaP256Signature::builder()
.r_sig(EccP256CurvePoint::CompressedY0(
b"12345678123456781234567812345678"
.to_vec()
.try_into()
.unwrap(),
))
.s_sig(
b"ABCDEFGHABCDEFGHABCDEFGHABCDEFGH"
.to_vec()
.try_into()
.unwrap(),
)
.build(),
))
.build(),
)))
.build();
round_trip!(
coer,
Ieee1609Dot2Data,
ieee1609dot2data,
&[
0x03, 0x81, 0x00, 0x40, 0x03, 0x80, 0x0F, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73,
0x20, 0x61, 0x20, 0x42, 0x53, 0x4D, 0x0D, 0x0A, 0x40, 0x01, 0x20, 0x11, 0x12, 0x13,
0x14, 0x15, 0x16, 0x17, 0x18, 0x80, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
0x80, 0x82, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34,
0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32,
0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
0x47, 0x48, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
]
);
}
#[test]
fn test_bsm_with_cert() {
round_trip!(
coer,
Ieee1609Dot2Data,
Ieee1609Dot2Data::builder()
.protocol_version(3)
.content(Ieee1609Dot2Content::SignedData(Box::new(
SignedData::builder()
.hash_id(HashAlgorithm::Sha256)
.tbs_data(
ToBeSignedData::builder()
.payload(
SignedDataPayload::builder()
.data(
Ieee1609Dot2Data::builder()
.protocol_version(3)
.content(Ieee1609Dot2Content::UnsecuredData(
Opaque("This is a BSM\r\n".as_bytes().into())
))
.build(),
)
.build()
.unwrap(),
)
.header_info(
HeaderInfo::builder()
.psid(Integer::from(32).into())
.generation_time(1_230_066_625_199_609_624.into())
.build(),
)
.build()
)
.signer(SignerIdentifier::Certificate(
vec![Certificate::from(
ImplicitCertificate::new(
CertificateBase::builder()
.version(3)
.r#type(CertificateType::Implicit)
.issuer(IssuerIdentifier::Sha256AndDigest(HashedId8(
"!\"#$%&'(".as_bytes().try_into().unwrap()
)))
.to_be_signed(
ToBeSignedCertificate::builder()
.id(CertificateId::LinkageData(
LinkageData::builder()
.i_cert(IValue::from(100))
.linkage_value(LinkageValue(
FixedOctetString::try_from(
b"123456789".as_slice()
)
.unwrap()
))
.group_linkage_value(
GroupLinkageValue::builder()
.j_value(
b"ABCD"
.as_slice()
.try_into()
.unwrap()
)
.value(
b"QRSTUVWXY"
.as_slice()
.try_into()
.unwrap()
)
.build()
)
.build()
))
.craca_id(HashedId3(
b"abc".as_slice().try_into().unwrap()
))
.crl_series(CrlSeries::from(70))
.validity_period(
ValidityPeriod::builder()
.start(81_828_384.into())
.duration(Duration::Hours(169))
.build()
)
.region(GeographicRegion::IdentifiedRegion(
vec![
IdentifiedRegion::CountryOnly(
UnCountryId::from(124)
),
IdentifiedRegion::CountryOnly(
UnCountryId::from(484)
),
IdentifiedRegion::CountryOnly(
UnCountryId::from(840)
)
]
.into()
))
.app_permissions(
vec![
PsidSsp {
psid: Integer::from(32).into(),
ssp: None
},
PsidSsp {
psid: Integer::from(38).into(),
ssp: None
}
]
.into()
)
.verify_key_indicator(
VerificationKeyIndicator::ReconstructionValue(
EccP256CurvePoint::CompressedY0(
FixedOctetString::from([
0x91u8, 0x92, 0x93, 0x94, 0x95,
0x96, 0x97, 0x98, 0x91, 0x92, 0x93,
0x94, 0x95, 0x96, 0x97, 0x98, 0x91,
0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x91, 0x92, 0x93, 0x94, 0x95,
0x96, 0x97, 0x98
])
)
)
)
.build()
.unwrap()
)
.build()
)
.unwrap()
)]
.into()
))
.signature(Signature::EcdsaNistP256(
EcdsaP256Signature::builder()
.r_sig(EccP256CurvePoint::CompressedY0(
b"12345678123456781234567812345678"
.as_slice()
.try_into()
.unwrap()
))
.s_sig(
b"ABCDEFGHABCDEFGHABCDEFGHABCDEFGH"
.as_slice()
.try_into()
.unwrap()
)
.build()
))
.build()
)))
.build(),
&[
0x03, 0x81, 0x00, 0x40, 0x03, 0x80, 0x0F, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73,
0x20, 0x61, 0x20, 0x42, 0x53, 0x4D, 0x0D, 0x0A, 0x40, 0x01, 0x20, 0x11, 0x12, 0x13,
0x14, 0x15, 0x16, 0x17, 0x18, 0x81, 0x01, 0x01, 0x00, 0x03, 0x01, 0x80, 0x21, 0x22,
0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x50, 0x80, 0x80, 0x00, 0x64, 0x31, 0x32, 0x33,
0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x51, 0x52, 0x53, 0x54,
0x55, 0x56, 0x57, 0x58, 0x59, 0x61, 0x62, 0x63, 0x00, 0x46, 0x04, 0xE0, 0x9A, 0x20,
0x84, 0x00, 0xA9, 0x83, 0x01, 0x03, 0x80, 0x00, 0x7C, 0x80, 0x01, 0xE4, 0x80, 0x03,
0x48, 0x01, 0x02, 0x00, 0x01, 0x20, 0x00, 0x01, 0x26, 0x81, 0x82, 0x91, 0x92, 0x93,
0x94, 0x95, 0x96, 0x97, 0x98, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x91,
0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x80, 0x82, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33,
0x34, 0x35, 0x36, 0x37, 0x38, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x31,
0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x41, 0x42, 0x43, 0x44, 0x45,
0x46, 0x47, 0x48, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48
]
);
}
#[test]
fn sample_choice() {
pub type Uint16 = u16;
#[derive(AsnType, Debug, Clone, Copy, Decode, Encode, PartialEq, Eq)]
#[rasn(choice, automatic_tags)]
pub enum Duration {
Microseconds(Uint16),
Milliseconds(Uint16),
Seconds(Uint16),
Minutes(Uint16),
Hours(Uint16),
SixtyHours(Uint16),
Years(Uint16),
}
let duration = Duration::Hours(10);
let output = rasn::coer::encode(&duration).unwrap();
assert_eq!(
rasn::coer::decode::<Duration>(output.as_slice()).unwrap(),
duration
);
}
}