Skip to content

Commit 54a0c20

Browse files
committed
asn1: Add support for IMPLICIT and EXPLICIT
Signed-off-by: Facundo Tuesca <[email protected]>
1 parent 3397275 commit 54a0c20

File tree

8 files changed

+197
-52
lines changed

8 files changed

+197
-52
lines changed

src/cryptography/hazmat/asn1/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
from cryptography.hazmat.asn1.asn1 import (
66
Default,
7+
Explicit,
78
GeneralizedTime,
9+
Implicit,
810
PrintableString,
911
UtcTime,
1012
decode_der,
@@ -14,7 +16,9 @@
1416

1517
__all__ = [
1618
"Default",
19+
"Explicit",
1720
"GeneralizedTime",
21+
"Implicit",
1822
"PrintableString",
1923
"UtcTime",
2024
"decode_der",

src/cryptography/hazmat/asn1/asn1.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,18 @@ def _is_union(field_type: type) -> bool:
6060

6161
def _extract_annotation(metadata: tuple) -> declarative_asn1.Annotation:
6262
default = None
63+
encoding = None
6364
for raw_annotation in metadata:
6465
if isinstance(raw_annotation, Default):
6566
default = raw_annotation.value
67+
elif isinstance(raw_annotation, Explicit):
68+
encoding = declarative_asn1.Encoding.Explicit(raw_annotation.tag)
69+
elif isinstance(raw_annotation, Implicit):
70+
encoding = declarative_asn1.Encoding.Implicit(raw_annotation.tag)
6671
else:
6772
raise TypeError(f"unsupported annotation: {raw_annotation}")
6873

69-
return declarative_asn1.Annotation(default=default)
74+
return declarative_asn1.Annotation(default=default, encoding=encoding)
7075

7176

7277
def _normalize_field_type(
@@ -194,6 +199,16 @@ class Default(typing.Generic[U]):
194199
value: U
195200

196201

202+
@dataclasses.dataclass(frozen=True)
203+
class Explicit:
204+
tag: int
205+
206+
207+
@dataclasses.dataclass(frozen=True)
208+
class Implicit:
209+
tag: int
210+
211+
197212
PrintableString = declarative_asn1.PrintableString
198213
UtcTime = declarative_asn1.UtcTime
199214
GeneralizedTime = declarative_asn1.GeneralizedTime

src/cryptography/hazmat/bindings/_rust/declarative_asn1.pyi

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,20 @@ class Type:
2121

2222
class Annotation:
2323
default: typing.Any | None
24+
encoding: Encoding | None
2425
def __new__(
2526
cls,
2627
default: typing.Any | None = None,
28+
encoding: Encoding | None = None,
2729
) -> Annotation: ...
2830
def is_empty(self) -> bool: ...
2931

32+
# Encoding is a Rust enum with tuple variants. For now, we express the type
33+
# annotations like this:
34+
class Encoding:
35+
Implicit: typing.ClassVar[type]
36+
Explicit: typing.ClassVar[type]
37+
3038
class AnnotatedType:
3139
inner: Type
3240
annotation: Annotation

src/rust/src/declarative_asn1/decode.rs

Lines changed: 49 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,41 @@ use pyo3::types::PyAnyMethods;
77

88
use crate::asn1::big_byte_slice_to_py_int;
99
use crate::declarative_asn1::types::{
10-
type_to_tag, AnnotatedType, GeneralizedTime, PrintableString, Type, UtcTime,
10+
type_to_tag, AnnotatedType, Encoding, GeneralizedTime, PrintableString, Type, UtcTime,
1111
};
1212
use crate::error::CryptographyError;
1313

1414
type ParseResult<T> = Result<T, CryptographyError>;
1515

16+
fn read_value<'a, T: asn1::SimpleAsn1Readable<'a>>(
17+
parser: &mut Parser<'a>,
18+
encoding: &Option<pyo3::Py<Encoding>>,
19+
) -> ParseResult<T> {
20+
let value = match encoding {
21+
Some(e) => match e.get() {
22+
Encoding::Implicit(n) => parser.read_implicit_element::<T>(*n),
23+
Encoding::Explicit(n) => parser.read_explicit_element::<T>(*n),
24+
},
25+
None => parser.read_element::<T>(),
26+
}?;
27+
Ok(value)
28+
}
29+
1630
fn decode_pybool<'a>(
1731
py: pyo3::Python<'a>,
1832
parser: &mut Parser<'a>,
33+
encoding: &Option<pyo3::Py<Encoding>>,
1934
) -> ParseResult<pyo3::Bound<'a, pyo3::types::PyBool>> {
20-
let value = parser.read_element::<bool>()?;
35+
let value = read_value::<bool>(parser, encoding)?;
2136
Ok(pyo3::types::PyBool::new(py, value).to_owned())
2237
}
2338

2439
fn decode_pyint<'a>(
2540
py: pyo3::Python<'a>,
2641
parser: &mut Parser<'a>,
42+
encoding: &Option<pyo3::Py<Encoding>>,
2743
) -> ParseResult<pyo3::Bound<'a, pyo3::types::PyInt>> {
28-
let value = parser.read_element::<asn1::BigInt<'a>>()?;
44+
let value = read_value::<asn1::BigInt<'a>>(parser, encoding)?;
2945
let pyint =
3046
big_byte_slice_to_py_int(py, value.as_bytes())?.cast_into::<pyo3::types::PyInt>()?;
3147
Ok(pyint)
@@ -34,33 +50,37 @@ fn decode_pyint<'a>(
3450
fn decode_pybytes<'a>(
3551
py: pyo3::Python<'a>,
3652
parser: &mut Parser<'a>,
53+
encoding: &Option<pyo3::Py<Encoding>>,
3754
) -> ParseResult<pyo3::Bound<'a, pyo3::types::PyBytes>> {
38-
let value = parser.read_element::<&[u8]>()?;
55+
let value = read_value::<&[u8]>(parser, encoding)?;
3956
Ok(pyo3::types::PyBytes::new(py, value))
4057
}
4158

4259
fn decode_pystr<'a>(
4360
py: pyo3::Python<'a>,
4461
parser: &mut Parser<'a>,
62+
encoding: &Option<pyo3::Py<Encoding>>,
4563
) -> ParseResult<pyo3::Bound<'a, pyo3::types::PyString>> {
46-
let value = parser.read_element::<asn1::Utf8String<'a>>()?;
64+
let value = read_value::<asn1::Utf8String<'a>>(parser, encoding)?;
4765
Ok(pyo3::types::PyString::new(py, value.as_str()))
4866
}
4967

5068
fn decode_printable_string<'a>(
5169
py: pyo3::Python<'a>,
5270
parser: &mut Parser<'a>,
71+
encoding: &Option<pyo3::Py<Encoding>>,
5372
) -> ParseResult<pyo3::Bound<'a, PrintableString>> {
54-
let value = parser.read_element::<asn1::PrintableString<'a>>()?.as_str();
73+
let value = read_value::<asn1::PrintableString<'a>>(parser, encoding)?.as_str();
5574
let inner = pyo3::types::PyString::new(py, value).unbind();
5675
Ok(pyo3::Bound::new(py, PrintableString { inner })?)
5776
}
5877

5978
fn decode_utc_time<'a>(
6079
py: pyo3::Python<'a>,
6180
parser: &mut Parser<'a>,
81+
encoding: &Option<pyo3::Py<Encoding>>,
6282
) -> ParseResult<pyo3::Bound<'a, UtcTime>> {
63-
let value = parser.read_element::<asn1::UtcTime>()?;
83+
let value = read_value::<asn1::UtcTime>(parser, encoding)?;
6484
let dt = value.as_datetime();
6585

6686
let inner = crate::x509::datetime_to_py_utc(py, dt)?
@@ -73,8 +93,9 @@ fn decode_utc_time<'a>(
7393
fn decode_generalized_time<'a>(
7494
py: pyo3::Python<'a>,
7595
parser: &mut Parser<'a>,
96+
encoding: &Option<pyo3::Py<Encoding>>,
7697
) -> ParseResult<pyo3::Bound<'a, GeneralizedTime>> {
77-
let value = parser.read_element::<asn1::GeneralizedTime>()?;
98+
let value = read_value::<asn1::GeneralizedTime>(parser, encoding)?;
7899
let dt = value.as_datetime();
79100

80101
let microseconds = match value.nanoseconds() {
@@ -102,11 +123,12 @@ pub(crate) fn decode_annotated_type<'a>(
102123
ann_type: &AnnotatedType,
103124
) -> ParseResult<pyo3::Bound<'a, pyo3::PyAny>> {
104125
let inner = ann_type.inner.get();
126+
let encoding = &ann_type.annotation.get().encoding;
105127

106128
// Handle DEFAULT annotation if field is not present (by
107129
// returning the default value)
108130
if let Some(default) = &ann_type.annotation.get().default {
109-
let expected_tag = type_to_tag(inner);
131+
let expected_tag = type_to_tag(inner, encoding);
110132
let next_tag = parser.peek_tag();
111133
if next_tag != Some(expected_tag) {
112134
return Ok(default.clone_ref(py).into_bound(py));
@@ -115,7 +137,7 @@ pub(crate) fn decode_annotated_type<'a>(
115137

116138
let decoded = match &inner {
117139
Type::Sequence(cls, fields) => {
118-
let seq_parse_result = parser.read_element::<asn1::Sequence<'_>>()?;
140+
let seq_parse_result = read_value::<asn1::Sequence<'_>>(parser, encoding)?;
119141

120142
seq_parse_result.parse(|d| -> ParseResult<pyo3::Bound<'a, pyo3::PyAny>> {
121143
let kwargs = pyo3::types::PyDict::new(py);
@@ -130,19 +152,27 @@ pub(crate) fn decode_annotated_type<'a>(
130152
})?
131153
}
132154
Type::Option(cls) => {
133-
let inner_tag = type_to_tag(cls.get().inner.get());
155+
let inner_tag = type_to_tag(cls.get().inner.get(), encoding);
134156
match parser.peek_tag() {
135-
Some(t) if t == inner_tag => decode_annotated_type(py, parser, cls.get())?,
157+
Some(t) if t == inner_tag => {
158+
// Since for optional types the annotations are enforced to be associated with the Option
159+
// (instead of the inner type), when decoding the inner type we add the annotations of the Option
160+
let inner_ann_type = AnnotatedType {
161+
inner: cls.get().inner.clone_ref(py),
162+
annotation: ann_type.annotation.clone_ref(py),
163+
};
164+
decode_annotated_type(py, parser, &inner_ann_type)?
165+
}
136166
_ => pyo3::types::PyNone::get(py).to_owned().into_any(),
137167
}
138168
}
139-
Type::PyBool() => decode_pybool(py, parser)?.into_any(),
140-
Type::PyInt() => decode_pyint(py, parser)?.into_any(),
141-
Type::PyBytes() => decode_pybytes(py, parser)?.into_any(),
142-
Type::PyStr() => decode_pystr(py, parser)?.into_any(),
143-
Type::PrintableString() => decode_printable_string(py, parser)?.into_any(),
144-
Type::UtcTime() => decode_utc_time(py, parser)?.into_any(),
145-
Type::GeneralizedTime() => decode_generalized_time(py, parser)?.into_any(),
169+
Type::PyBool() => decode_pybool(py, parser, encoding)?.into_any(),
170+
Type::PyInt() => decode_pyint(py, parser, encoding)?.into_any(),
171+
Type::PyBytes() => decode_pybytes(py, parser, encoding)?.into_any(),
172+
Type::PyStr() => decode_pystr(py, parser, encoding)?.into_any(),
173+
Type::PrintableString() => decode_printable_string(py, parser, encoding)?.into_any(),
174+
Type::UtcTime() => decode_utc_time(py, parser, encoding)?.into_any(),
175+
Type::GeneralizedTime() => decode_generalized_time(py, parser, encoding)?.into_any(),
146176
};
147177

148178
match &ann_type.annotation.get().default {

src/rust/src/declarative_asn1/encode.rs

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,21 @@ use asn1::{SimpleAsn1Writable, Writer};
66
use pyo3::types::PyAnyMethods;
77

88
use crate::declarative_asn1::types::{
9-
AnnotatedType, AnnotatedTypeObject, GeneralizedTime, PrintableString, Type, UtcTime,
9+
AnnotatedType, AnnotatedTypeObject, Encoding, GeneralizedTime, PrintableString, Type, UtcTime,
1010
};
1111

1212
fn write_value<T: SimpleAsn1Writable>(
1313
writer: &mut Writer<'_>,
1414
value: &T,
15+
encoding: &Option<pyo3::Py<Encoding>>,
1516
) -> Result<(), asn1::WriteError> {
16-
writer.write_element(value)
17+
match encoding {
18+
Some(e) => match e.get() {
19+
Encoding::Implicit(tag) => writer.write_implicit_element(value, *tag),
20+
Encoding::Explicit(tag) => writer.write_explicit_element(value, *tag),
21+
},
22+
None => writer.write_element(value),
23+
}
1724
}
1825

1926
impl asn1::Asn1Writable for AnnotatedTypeObject<'_> {
@@ -37,6 +44,7 @@ impl asn1::Asn1Writable for AnnotatedTypeObject<'_> {
3744
}
3845
}
3946

47+
let encoding = &annotated_type.annotation.get().encoding;
4048
let inner = annotated_type.inner.get();
4149
match &inner {
4250
Type::Sequence(_cls, fields) => write_value(
@@ -60,14 +68,20 @@ impl asn1::Asn1Writable for AnnotatedTypeObject<'_> {
6068
}
6169
Ok(())
6270
}),
71+
encoding,
6372
),
6473
Type::Option(cls) => {
6574
if !value.is_none() {
66-
let object = AnnotatedTypeObject {
67-
annotated_type: cls.get(),
75+
let inner_object = AnnotatedTypeObject {
76+
annotated_type: &AnnotatedType {
77+
inner: cls.get().inner.clone_ref(py),
78+
// Since for optional types the annotations are enforced to be associated with the Option
79+
// (instead of the inner type), when encoding the inner type we add the annotations of the Option
80+
annotation: annotated_type.annotation.clone_ref(py),
81+
},
6882
value,
6983
};
70-
object.write(writer)
84+
inner_object.write(writer)
7185
} else {
7286
// Missing OPTIONAL values are omitted from DER encoding
7387
Ok(())
@@ -77,26 +91,26 @@ impl asn1::Asn1Writable for AnnotatedTypeObject<'_> {
7791
let val: bool = value
7892
.extract()
7993
.map_err(|_| asn1::WriteError::AllocationError)?;
80-
write_value(writer, &val)
94+
write_value(writer, &val, encoding)
8195
}
8296
Type::PyInt() => {
8397
let val: i64 = value
8498
.extract()
8599
.map_err(|_| asn1::WriteError::AllocationError)?;
86-
write_value(writer, &val)
100+
write_value(writer, &val, encoding)
87101
}
88102
Type::PyBytes() => {
89103
let val: &[u8] = value
90104
.extract()
91105
.map_err(|_| asn1::WriteError::AllocationError)?;
92-
write_value(writer, &val)
106+
write_value(writer, &val, encoding)
93107
}
94108
Type::PyStr() => {
95109
let val: pyo3::pybacked::PyBackedStr = value
96110
.extract()
97111
.map_err(|_| asn1::WriteError::AllocationError)?;
98112
let asn1_string: asn1::Utf8String<'_> = asn1::Utf8String::new(&val);
99-
write_value(writer, &asn1_string)
113+
write_value(writer, &asn1_string, encoding)
100114
}
101115
Type::PrintableString() => {
102116
let val: &pyo3::Bound<'_, PrintableString> = value
@@ -110,7 +124,7 @@ impl asn1::Asn1Writable for AnnotatedTypeObject<'_> {
110124
let printable_string: asn1::PrintableString<'_> =
111125
asn1::PrintableString::new(&inner_str)
112126
.ok_or(asn1::WriteError::AllocationError)?;
113-
write_value(writer, &printable_string)
127+
write_value(writer, &printable_string, encoding)
114128
}
115129
Type::UtcTime() => {
116130
let val: &pyo3::Bound<'_, UtcTime> = value
@@ -121,7 +135,7 @@ impl asn1::Asn1Writable for AnnotatedTypeObject<'_> {
121135
.map_err(|_| asn1::WriteError::AllocationError)?;
122136
let utc_time =
123137
asn1::UtcTime::new(datetime).map_err(|_| asn1::WriteError::AllocationError)?;
124-
write_value(writer, &utc_time)
138+
write_value(writer, &utc_time, encoding)
125139
}
126140
Type::GeneralizedTime() => {
127141
let val: &pyo3::Bound<'_, GeneralizedTime> = value
@@ -134,7 +148,7 @@ impl asn1::Asn1Writable for AnnotatedTypeObject<'_> {
134148
let nanoseconds = microseconds.map(|m| m * 1000);
135149
let generalized_time = asn1::GeneralizedTime::new(datetime, nanoseconds)
136150
.map_err(|_| asn1::WriteError::AllocationError)?;
137-
write_value(writer, &generalized_time)
151+
write_value(writer, &generalized_time, encoding)
138152
}
139153
}
140154
}

0 commit comments

Comments
 (0)