#ber-der #codec #asn-1 #der #ber #asn1-der

no-std rasn

安全的 no_std ASN.1 编解码框架

53 个版本 (15 个重大更新)

0.16.5 2024 年 8 月 23 日
0.16.0 2024 年 7 月 17 日
0.13.1 2024 年 3 月 21 日
0.12.4 2023 年 12 月 4 日
0.0.0 2019 年 6 月 22 日

编码 类别下排名 #60

Download history 5084/week @ 2024-05-03 7170/week @ 2024-05-10 18987/week @ 2024-05-17 21802/week @ 2024-05-24 28777/week @ 2024-05-31 21656/week @ 2024-06-07 15593/week @ 2024-06-14 10282/week @ 2024-06-21 7269/week @ 2024-06-28 21478/week @ 2024-07-05 38189/week @ 2024-07-12 45763/week @ 2024-07-19 26710/week @ 2024-07-26 42752/week @ 2024-08-02 53984/week @ 2024-08-09 26765/week @ 2024-08-16

每月下载量 154,610
用于 38 包(20 个直接使用)

MIT/Apache

695KB
17K SLoC

Rasn

crates.io Help Wanted Lines Of Code Documentation

欢迎来到 rasn(发音为 "raisin"),一个安全的 #[no_std] ASN.1 编解码框架。这使您能够安全地创建、共享和处理从不同编码规则转换的 ASN.1 数据类型。如果您对 ASN.1 和 BER/DER 等编码格式不熟悉,我建议在继续之前阅读 Let's Encrypt 的"A Warm Welcome to ASN.1 and DER" 作为快速介绍。简而言之,它是一种“接口描述语言”(和数据模型),以及为该模型定义的一组编码格式(称为规则)。它最初在 20 世纪 80 年代末期设计,并在整个行业中使用,特别是在电信和密码学领域。

特性

抽象编解码数据模型

目前已经有相当多的 ASN.1 相关的 Rust 包,但是它们目前都针对单一格式,甚至单一标准,这使得共享和重用 ASN.1 中指定的标准变得困难。现在,有了 rasn 的抽象模型,您可以将 ASN.1 数据类型构建为可以与任何编码器或解码器一起工作的包,无论其底层编码规则是 BER、CER、DER 还是您自己的自定义编码。

#[no_std] 支持

Rasn 完全是 #[no_std],因此您可以在支持 alloc 的任何 Rust 目标平台上共享相同的 ASN.1 实现。

丰富的数据类型

Rasn 目前支持 ASN.1 几乎所有的数据类型。 rasn 使用流行的社区库,如 bitvecbyteschrono,以及提供一些自己的数据类型。查看 types 模块,了解目前可用的内容。

安全编解码器

编码器和解码器已经用100%安全的Rust编写,并使用美国模糊跳蛙++进行模糊测试,以确保解码器可以正确处理随机输入,如果有效,编码器可以正确重新编码该值。

支持的编解码器

  • 基本编码规则(BER)
  • 规范编码规则(CER)
  • 区分编码规则(DER)
  • 对齐打包编码规则(APER)
  • 非对齐打包编码规则(UPER)
  • JSON编码规则(JER)
  • 八位编码规则(OER)
  • 规范八位编码规则(COER)

RFC实现

Rasn还提供了使用rasn框架实现的多个IETF RFC,可直接使用。这些crate为必要的数据类型提供了强类型定义。与rasn一样,它们是#[no_std],并且是传输层和编码规则无关的。

强大的派生宏

使用所有特质的派生等效物轻松建模您的结构和枚举。这些宏提供自动实现,确保您的模型在编译时是有效的ASN.1类型。但要解释这一点,首先我们必须解释…

工作原理

编解码器API已被设计为易于使用、安全且难以误用。最常见的错误是处理长度和确保其正确编码和解码。在rasn中,这一点完全抽象化,让您专注于抽象模型。让我们看看解码简单的自定义SEQUENCE类型的样子。

Person ::= SEQUENCE {
  age INTEGER,
  name UTF8String
}

我们希望将其映射到以下等效的Rust代码。

struct Person {
    age: rasn::types::Integer,
    name: String, // or rasn::types::Utf8String
}

实现特剧行为

在建模ASN.1数据类型时,我们需要实现三个特剧行为。对于将编码规则转换为和从编码规则转换的DecodeEncode,以及共享的AsnType特剧行为;它定义了一些需要提供给编码器和解码器的关联数据。目前我们唯一要定义的是用于识别我们类型的标签。

# struct Person;
use rasn::{AsnType, Tag};

impl AsnType for Person {
    // Default tag for sequences.
    const TAG: Tag = Tag::SEQUENCE;
}

接下来是DecodeEncode特剧行为。这两个是对方的镜像,并且都提供了一个提供的方法(decode/encode)和一个必需的方法(decode_with_tag/encode_with_tag)。由于在ASN.1中几乎每种类型都可以隐式标记,允许任何人覆盖与类型关联的标记,所以必需的*_with_tag方法要求实现者正确处理这种情况,而提供的方法只是简单地将类型关联的AsnType::TAG作为参数调用*_with_tag方法。让我们看看Person的编解码器实现。

# use rasn::{AsnType, types::{Constructed, fields::{Field, Fields}}};
# struct Person { name: Utf8String, age: Integer }
# impl AsnType for Person { const TAG: Tag = Tag::SEQUENCE; }
# impl Constructed for Person {
#     const FIELDS: Fields = Fields::from_static(&[
#          Field::new_required(Utf8String::TAG, Utf8String::TAG_TREE, "age"),
#          Field::new_required(Integer::TAG, Integer::TAG_TREE, "name"),
#     ]);
# }
use rasn::{prelude::*, types::{Integer, Utf8String}};

impl Decode for Person {
    fn decode_with_tag_and_constraints<D: Decoder>(decoder: &mut D, tag: Tag, constraints: Constraints) -> Result<Self, D::Error> {
        // Accepts a closure that decodes the contents of the sequence.
        decoder.decode_sequence(tag, None::<fn () -> Self>, |decoder| {
            let age = Integer::decode(decoder)?;
            let name = Utf8String::decode(decoder)?;
            Ok(Self { age, name })
        })
    }
}

impl Encode for Person {
    fn encode_with_tag_and_constraints<E: Encoder>(&self, encoder: &mut E, tag: Tag, constraints: Constraints) -> Result<(), E::Error> {
        // Accepts a closure that encodes the contents of the sequence.
        encoder.encode_sequence::<Self, _>(tag, |encoder| {
            self.age.encode(encoder)?;
            self.name.encode(encoder)?;
            Ok(())
        })?;

        Ok(())
    }
}

这就完成了!我们刚刚创建了一个新的ASN.1,它可以被编码和解码为BER、CER和DER;在编码和解码过程中,我们不需要检查标签、长度或字符串是原始的还是构造的编码。所有那些讨厌的编码规则细节都被完全抽象化,因此您的类型只需处理如何映射到和从ASN.1的数据模型。

由于所有的实际转换代码都被隔离到编解码器实现中,您可以知道您的模型始终是安全的。API也被设计用来防止您犯导致无效编码的常见逻辑错误。例如,如果我们回顾一下我们的 Encode 实现,如果我们忘记在 encode_sequence 中使用我们得到的编码器,而是尝试使用父编码器会怎么样呢?

error[E0501]: cannot borrow `*encoder` as mutable because previous closure requires unique access
   --> tests/derive.rs:122:9
    |
122 |           encoder.encode_sequence(tag, |sequence| {
    |           ^       ---------------      ---------- closure construction occurs here
    |           |       |
    |  _________|       first borrow later used by call
    | |
123 | |             self.age.encode(encoder)?;
    | |                             ------- first borrow occurs due to use of `encoder` in closure
124 | |             self.name.encode(sequence)?;
125 | |             Ok(())
126 | |         })?;
    | |__________^ second borrow occurs here

error[E0500]: closure requires unique access to `encoder` but it is already borrowed
   --> tests/derive.rs:122:38
    |
122 |         encoder.encode_sequence(tag, |sequence| {
    |         ------- ---------------      ^^^^^^^^^^ closure construction occurs here
    |         |       |
    |         |       first borrow later used by call
    |         borrow occurs here
123 |             self.age.encode(encoder)?;
    |                             ------- second borrow occurs due to use of `encoder` in closure

我们的代码编译失败了!这在当前情况下是个好事,因为我们没有机会因为忘记更改变量名称而导致内容被错误地编码。这些所有权语义还意味着一个 Encoder 在实现中不会意外地多次编码序列的内容。让我们看看如何更进一步。

使用宏的编译安全ASN.1

到目前为止,我们已经展示了rasn的API如何采取措施确保安全并防止意外创建无效模型。然而,在命令式API中很难涵盖所有内容。关于ASN.1的一个重要理解,在上述示例中并不明显的是,在ASN.1中,所有类型都可以通过一个标签(本质上两个数字,例如 INTEGER 的标签是 0, 2)来识别)。字段和变体名称在大多数编码规则中不传输,因此这个标签也用于在 SEQUENCECHOICE 中识别字段或变体。这意味着在ASN.1结构体或枚举中,每个字段和变体 必须 有一个独特的标签,整个类型才能被认为是有效的。例如;如果我们把 Person 中的 age 改成一个 String,就像下面这样,它将是不有效的ASN.1,即使它编译和运行正确,我们必须使用不同的类型或覆盖 age 的标签,使其与 name 的标签不同。当自己实现 AsnType 特性时,必须手动检查此要求,但正如我们将看到的,您通常不需要这样做。

rasn 包含一系列 derive 宏,使您能够以声明性方式实现 ASN.1 模型实现。 EncodeDecode 宏将基本自动生成我们之前展示的实现,但真正的魔法在于 AsnType derive 宏。多亏了 static-assertions crate 和最近的 const fn 发展;AsnType derive 不仅可以生成您的 AsnType 实现,它还会生成一个在 编译时 断言每个字段或变体具有独特标签的检查。这意味着现在如果由于某种原因我们对 person 中的一个类型进行了更改,我们不需要重新检查我们的模型是否仍然有效,编译器会为我们处理这个问题。

// Invalid
#[derive(rasn::AsnType)]
struct Person {
    age: Option<String>,
    name: Option<String>,
}

现在在尝试编译上述定义时,我们会得到以下错误。

error[E0080]: evaluation of constant value failed
   --> tests/derive.rs:146:10
    |
146 | #[derive(rasn::AsnType)]
    |          ^^^^^^^^^^^^^ the evaluated program panicked at 'Person's fields is not a valid order of ASN.1 tags, ensure that your field's tags and OPTIONAL
s are correct.', tests/derive.rs:146:10
    |
    = note: this error originates in the macro `$crate::panic::panic_2015` (in Nightly builds, run with -Z macro-backtrace for more info)

在编译时验证您的模型可以使您在编写 ASN.1 代码时无需担心无意中更改后台的某些内容。我敢打赌你现在可能想知道,我们应该如何有一个包含两个字段的 struct?答案是幸运的很简单,您只需添加 #[rasn(tag)] 属性来覆盖一个或多个类型的标签。然而,我们可以更进一步,因为在 ASN.1 中存在 AUTOMATIC TAGS 的概念,这本质上告诉您的 ASN.1 编译器自动为您 ASN.1 定义生成不同的标签。现在,使用 rasn,您可以在 Rust 中实现这一点!将 #[rasn(automatic_tags)] 应用到容器将自动生成标签,这将应用您从 ASN.1 编译器期望的相同自动标签转换。

use rasn::AsnType;

// Valid
#[derive(AsnType)]
struct Person {
    #[rasn(tag(context, 0))] // or just #[rasn(tag(0))]
    age: Option<String>,
    name: Option<String>,
}

// Also valid
#[derive(AsnType)]
#[rasn(automatic_tags)]
struct Person2 {
    age: Option<String>,
    name: Option<String>,
}

参考

以下表格提供了一系列示例,展示了如何使用 rasn 声明数据类型。

ASN1 rasn
类型别名
Test-type-b ::= BOOLEAN
Test-type-a ::= Test-type-b
// either
use rasn::prelude::*;

type TestTypeB = bool;

type TestTypeA = TestTypeB;
// or 
use rasn::prelude::*;

#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeB(pub bool);

/// or
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeA(pub TestTypeB);
BOOLEAN 类型
Test-type-a ::= BOOLEAN
// either
use rasn::prelude::*;

#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeA(pub bool);
// or
use rasn::prelude::*;

type TestTypeA = bool;
NULL 类型
Test-type-a ::= NULL
// either
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
struct TestTypeA;
// or
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeA(());
// or
use rasn::prelude::*;

type TestTypeA = ();
INTEGER 类型
Test-type-a ::= INTEGER
use rasn::prelude::*;
// either
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeA(pub u8 /* or any other rust integer type */);
// or
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeA(pub Integer);
// or
use rasn::prelude::*;
type TestTypeA = Integer;
// or
use rasn::prelude::*;
type TestTypeA = u8; // or any other rust integer type
单值约束
Test-type-a ::= INTEGER (8)
// either
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, value("8"))]
struct TestTypeA(pub u8);
// or
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, value("8"))]
struct TestTypeA(pub Integer);
值范围约束
Test-type-a ::= INTEGER (-8..360)
Test-type-b ::= INTEGER (MIN..360)
Test-type-c ::= INTEGER (42..MAX)
use rasn::prelude::*;
/// of course a primitive rust integer would still work in these examples
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, value("-8..=360"))]
struct TestTypeA(pub Integer);

#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, value("..=360"))]
struct TestTypeB(pub Integer);

#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, value("42..="))]
struct TestTypeC(pub Integer);
可扩展值约束
Test-type-a ::= INTEGER (42,...)
Test-type-b ::= INTEGER (1..360,...)
use rasn::prelude::*;
/// of course a primitive rust integer would still work in these examples
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, value("42", extensible))]
struct TestTypeA(pub Integer);

#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, value("1..=360", extensible))]
struct TestTypeB(pub Integer);
ENUMERATED 类型
Test-type-a ::= ENUMERATED { seed, grape, raisin }
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode, Copy, Clone, PartialEq, Debug)]
#[rasn(enumerated, automatic_tags)] /// See below
enum TestTypeA {
    Seed,
    Grape,
    Raisin
}
可扩展 ENUMERATED 类型
Test-type-a ::= ENUMERATED { seed, grape, ..., raisin }
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode, Copy, Clone, PartialEq, Debug)]
#[rasn(enumerated, automatic_tags)] /// See below
#[non_exhaustive]
enum TestTypeA {
    Seed,
    Grape,
    #[rasn(extension_addition)]
    Raisin
}
AUTOMATIC TAGS 环境
TestModule DEFINITIONS AUTOMATIC TAGS ::=
BEGIN
Test-type-a ::= ENUMERATED { seed, grape, raisin }
Test-type-b ::= ENUMERATED { juice, wine, grappa }
END
use rasn::prelude::*;
/// The tagging encironment has to be declared for every rasn-annotated struct or enum
/// There is no implicit extensibility
#[derive(AsnType, Decode, Encode, Copy, Clone, PartialEq, Debug)]
#[rasn(enumerated, automatic_tags)]
enum TestTypeB {
    Juice,
    Wine,
    Grappa
}

#[derive(AsnType, Decode, Encode, Copy, Clone, PartialEq, Debug)]
#[rasn(enumerated, automatic_tags)]
enum TestTypeA {
    Seed,
    Grape,
    Raisin
}
EXPLICIT TAGS 环境
TestModule DEFINITIONS EXPLICIT TAGS ::=
BEGIN
Test-type-a ::= [APPLICATION 1] ENUMERATED { seed, grape, raisin }
Test-type-b ::= [APPLICATION 2] ENUMERATED { juice, wine, grappa }
END
use rasn::prelude::*;
/// The tagging encironment has to be declared for every rasn-annotated struct or enum
/// There is no implicit extensibility
#[derive(AsnType, Decode, Encode, Copy, Clone, PartialEq, Debug)]
#[rasn(enumerated, tag(explicit(application, 1)))]
enum TestTypeB {
    #[rasn(tag(explicit(0)))]
    Juice,
    #[rasn(tag(explicit(1)))]
    Wine,
    #[rasn(tag(explicit(2)))]
    Grappa
}

#[derive(AsnType, Decode, Encode, Copy, Clone, PartialEq, Debug)]
#[rasn(enumerated, tag(explicit(application, 2)))]
enum TestTypeA {
    #[rasn(tag(explicit(0)))]
    Seed,
    #[rasn(tag(explicit(1)))]
    Grape,
    #[rasn(tag(explicit(2)))]
    Raisin
}
IMPLICIT TAGS 环境
TestModule DEFINITIONS IMPLICIT TAGS ::=
BEGIN
Test-type-a ::= [APPLICATION 1] ENUMERATED { seed, grape, raisin }
Test-type-b ::= [APPLICATION 2] ENUMERATED { juice, wine, grappa }
END
use rasn::prelude::*;
/// The tagging encironment has to be declared for every rasn-annotated struct or enum
/// There is no implicit extensibility
#[derive(AsnType, Decode, Encode, Copy, Clone, PartialEq, Debug)]
#[rasn(enumerated, tag(application, 1))]
enum TestTypeB {
    Juice = 0,
    Wine = 1,
    Grappa = 2
}

#[derive(AsnType, Decode, Encode, Copy, Clone, PartialEq, Debug)]
#[rasn(enumerated, tag(application, 2))]
enum TestTypeA {
    Seed = 0,
    Grape = 1,
    Raisin = 2
}
CHOICE 类型
Test-type-a ::= CHOICE {
    seed BOOLEAN,
    grape BIT STRING SIZE(1,...),
    raisin OCTET STRING
}

Test-type-b ::= CHOICE { 
    juice INTEGER (0..3,...), 
    wine OCTET STRING,
    ...,
    grappa INTEGER 
}
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
#[rasn(choice, automatic_tags)]
enum TestTypeA {
    Seed(bool),
    #[rasn(size("1", extensible))]
    Grape(BitString),
    Raisin(OctetString)
}

#[derive(AsnType, Decode, Encode)]
#[rasn(choice, automatic_tags)]
#[non_exhaustive]
enum TestTypeB {
    #[rasn(value("0..3", extensible))]
    Juice(Integer),
    Wine(OctetString),
    #[rasn(extension_addition)]
    Grappa(Integer)
}

SEQUENCE 类型
Test-type-a ::= SEQUENCE { 
    juice INTEGER (0..3,...), 
    wine OCTET STRING,
    ...,
    grappa INTEGER OPTIONAL,
    water BIT STRING (SIZE(1)) OPTIONAL
}
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
#[rasn(automatic_tags)]
#[non_exhaustive]
struct TestTypeA {
    #[rasn(value("0..3", extensible))]
    juice: Integer,
    wine: OctetString,
    #[rasn(extension_addition)]
    grappa: Option<Integer>,
    #[rasn(extension_addition, size("1"))]
    water: Option<BitString>
}

SET 类型
Test-type-a ::= SET { 
    seed NULL,
    grape BOOLEAN,
    raisin INTEGER
}
use rasn::prelude::*;
/// the SET declaration is basically identical to a SEQUENCE declaration, 
/// except for the `set` annotation
#[derive(AsnType, Decode, Encode)]
#[rasn(set, automatic_tags)]
struct TestTypeA {
    seed: (),
    grape: bool,
    raisin: Integer
}

重命名字段
Test-type-a ::= SEQUENCE { 
    notQuiteRustCase INTEGER
}
use rasn::prelude::*;

#[derive(AsnType, Decode, Encode)]
#[rasn(automatic_tags, identifier = "Test-type-a")]
struct TestTypeA {
    #[rasn(identifier = "notQuiteRustCase")]
    rust_case_indeed: Integer
}

可选和默认字段
Test-type-a ::= SEQUENCE { 
    seed BOOLEAN DEFAULT TRUE,
    grape INTEGER OPTIONAL,
    raisin INTEGER DEFAULT 1
}
use rasn::prelude::*;
/// DEFAULTs are provided via linked helper functions
#[derive(AsnType, Decode, Encode)]
#[rasn(automatic_tags)]
struct TestTypeA {
    #[rasn(default = "default_seed")] 
    seed: bool,
    grape: Option<Integer>,
    #[rasn(default = "default_raisin")]
    raisin: Integer
}

fn default_seed() -> bool {
    true
}

fn default_raisin() -> Integer {
    1.into()
}
SEQUENCE OF 类型
Test-type-a ::= SEQUENCE OF BOOLEAN
Test-type-b ::= SEQUENCE OF INTEGER(1,...)
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeA(pub SequenceOf<bool>);

/// Constrained inner primitive types need to be wrapped in a helper newtype
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, value("1", extensible))]
struct InnerTestTypeB(pub Integer);

#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeB(pub SequenceOf<InnerTestTypeB>);
字符字符串类型
Test-type-a ::= UTF8String
use rasn::prelude::*;
/// the other charater types supported by rasn behave exactly the same:
/// NumericString, VisibleString, Ia5String, TeletexString, GeneralString, BmpString, PrintableString
/// (and also for BIT STRING and OCTET STRING)
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeA(pub Utf8String);
BIT STRING 类型
Test-type-a ::= BIT STRING
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeA(pub BitString);
OCTET STRING 类型
Test-type-a ::= OCTET STRING
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate)]
struct TestTypeA(pub OctetString);
大小约束
Test-type-a ::= UTF8String (SIZE (42,...))
Test-type-b ::= SEQUENCE (SIZE (1..8)) OF BOOLEAN
use rasn::prelude::*;
/// The size constraint definition behaves similar to the value definition (see above)
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, size("42", extensible))]
struct TestTypeA(pub Utf8String);

#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, size("1..=8"))]
struct TestTypeB(pub SequenceOf<bool>);
允许的字母表约束
Test-type-a ::= UTF8String (FROM ("A".."Z"))
use rasn::prelude::*;
#[derive(AsnType, Decode, Encode)]
#[rasn(delegate, from("\u{0041}..\u{005A}"))]
struct TestTypeA(pub Utf8String);

赞助商

该项目是通过 NLnet 建立的 NGI Assure Fund 资助的,NLnet 由欧洲委员会的 Next Generation Internet 计划提供资金支持,在 DG Communications Networks、Content and Technology 的指导下,根据第 957073 号资助协议。

免责声明

软件按原样提供,作者在此软件方面放弃所有保证,包括所有默示的适销性和适用性保证。在任何情况下,作者均不对任何特殊、直接、间接或后果性损害或任何损害(无论因合同、疏忽或其他侵权行为而引起或与之相关)承担责任,包括但不限于使用或数据或利润的损失。

依赖性

~7MB
~144K SLoC