Skip to content

Commit 5bda45c

Browse files
authored
asn1: Add SIZE support to PrintableString (#14037)
Signed-off-by: Facundo Tuesca <[email protected]>
1 parent d08934d commit 5bda45c

File tree

4 files changed

+120
-5
lines changed

4 files changed

+120
-5
lines changed

src/cryptography/hazmat/asn1/asn1.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,12 +107,13 @@ def _normalize_field_type(
107107

108108
if annotation.size is not None and (
109109
get_type_origin(field_type) is not builtins.list
110-
and field_type not in (builtins.bytes, builtins.str, BitString)
110+
and field_type
111+
not in (builtins.bytes, builtins.str, BitString, PrintableString)
111112
):
112113
raise TypeError(
113114
f"field {field_name} has a SIZE annotation, but SIZE annotations "
114115
f"are only supported for fields of types: [SEQUENCE OF, "
115-
"BIT STRING, OCTET STRING, UTF8String]"
116+
"BIT STRING, OCTET STRING, UTF8String, PrintableString]"
116117
)
117118

118119
if hasattr(field_type, "__asn1_root__"):

src/rust/src/declarative_asn1/decode.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,10 @@ fn decode_pystr<'a>(
7272
fn decode_printable_string<'a>(
7373
py: pyo3::Python<'a>,
7474
parser: &mut Parser<'a>,
75-
encoding: &Option<pyo3::Py<Encoding>>,
75+
annotation: &Annotation,
7676
) -> ParseResult<pyo3::Bound<'a, PrintableString>> {
77-
let value = read_value::<asn1::PrintableString<'a>>(parser, encoding)?.as_str();
77+
let value = read_value::<asn1::PrintableString<'a>>(parser, &annotation.encoding)?.as_str();
78+
check_size_constraint(&annotation.size, value.len(), "PrintableString")?;
7879
let inner = pyo3::types::PyString::new(py, value).unbind();
7980
Ok(pyo3::Bound::new(py, PrintableString { inner })?)
8081
}
@@ -219,7 +220,7 @@ pub(crate) fn decode_annotated_type<'a>(
219220
Type::PyInt() => decode_pyint(py, parser, encoding)?.into_any(),
220221
Type::PyBytes() => decode_pybytes(py, parser, annotation)?.into_any(),
221222
Type::PyStr() => decode_pystr(py, parser, annotation)?.into_any(),
222-
Type::PrintableString() => decode_printable_string(py, parser, encoding)?.into_any(),
223+
Type::PrintableString() => decode_printable_string(py, parser, annotation)?.into_any(),
223224
Type::IA5String() => decode_ia5_string(py, parser, encoding)?.into_any(),
224225
Type::UtcTime() => decode_utc_time(py, parser, encoding)?.into_any(),
225226
Type::GeneralizedTime() => decode_generalized_time(py, parser, encoding)?.into_any(),

src/rust/src/declarative_asn1/encode.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,8 @@ impl asn1::Asn1Writable for AnnotatedTypeObject<'_> {
145145
.inner
146146
.to_cow(py)
147147
.map_err(|_| asn1::WriteError::AllocationError)?;
148+
check_size_constraint(&annotation.size, inner_str.len(), "PrintableString")
149+
.map_err(|_| asn1::WriteError::AllocationError)?;
148150
let printable_string: asn1::PrintableString<'_> =
149151
asn1::PrintableString::new(&inner_str)
150152
.ok_or(asn1::WriteError::AllocationError)?;

tests/hazmat/asn1/test_serialization.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,117 @@ class Example:
916916
]
917917
)
918918

919+
def test_ok_printablestring_size_restriction(self) -> None:
920+
@asn1.sequence
921+
@_comparable_dataclass
922+
class Example:
923+
a: Annotated[asn1.PrintableString, asn1.Size(min=1, max=4)]
924+
925+
assert_roundtrips(
926+
[
927+
(
928+
Example(a=asn1.PrintableString("abcd")),
929+
b"\x30\x06\x13\x04abcd",
930+
)
931+
]
932+
)
933+
934+
def test_ok_printablestring_size_restriction_no_max(self) -> None:
935+
@asn1.sequence
936+
@_comparable_dataclass
937+
class Example:
938+
a: Annotated[asn1.PrintableString, asn1.Size(min=1, max=None)]
939+
940+
assert_roundtrips(
941+
[
942+
(
943+
Example(a=asn1.PrintableString("abcd")),
944+
b"\x30\x06\x13\x04abcd",
945+
)
946+
]
947+
)
948+
949+
def test_ok_printablestring_size_restriction_exact(self) -> None:
950+
@asn1.sequence
951+
@_comparable_dataclass
952+
class Example:
953+
a: Annotated[asn1.PrintableString, asn1.Size.exact(4)]
954+
955+
assert_roundtrips(
956+
[
957+
(
958+
Example(a=asn1.PrintableString("abcd")),
959+
b"\x30\x06\x13\x04abcd",
960+
)
961+
]
962+
)
963+
964+
def test_fail_printablestring_size_too_big(self) -> None:
965+
@asn1.sequence
966+
@_comparable_dataclass
967+
class Example:
968+
a: Annotated[asn1.PrintableString, asn1.Size(min=1, max=2)]
969+
970+
with pytest.raises(
971+
ValueError,
972+
match=re.escape(
973+
"PrintableString has size 4, expected size in [1, 2]"
974+
),
975+
):
976+
asn1.decode_der(
977+
Example,
978+
b"\x30\x06\x13\x04abcd",
979+
)
980+
981+
with pytest.raises(
982+
ValueError,
983+
):
984+
asn1.encode_der(Example(a=asn1.PrintableString("abcd")))
985+
986+
def test_fail_printablestring_size_too_small(self) -> None:
987+
@asn1.sequence
988+
@_comparable_dataclass
989+
class Example:
990+
a: Annotated[asn1.PrintableString, asn1.Size(min=5, max=6)]
991+
992+
with pytest.raises(
993+
ValueError,
994+
match=re.escape(
995+
"PrintableString has size 4, expected size in [5, 6]"
996+
),
997+
):
998+
asn1.decode_der(
999+
Example,
1000+
b"\x30\x06\x13\x04abcd",
1001+
)
1002+
1003+
with pytest.raises(
1004+
ValueError,
1005+
):
1006+
asn1.encode_der(Example(a=asn1.PrintableString("abcd")))
1007+
1008+
def test_fail_printablestring_size_not_exact(self) -> None:
1009+
@asn1.sequence
1010+
@_comparable_dataclass
1011+
class Example:
1012+
a: Annotated[asn1.PrintableString, asn1.Size.exact(5)]
1013+
1014+
with pytest.raises(
1015+
ValueError,
1016+
match=re.escape(
1017+
"PrintableString has size 4, expected size in [5, 5]"
1018+
),
1019+
):
1020+
asn1.decode_der(
1021+
Example,
1022+
b"\x30\x06\x13\x04abcd",
1023+
)
1024+
1025+
with pytest.raises(
1026+
ValueError,
1027+
):
1028+
asn1.encode_der(Example(a=asn1.PrintableString("abcd")))
1029+
9191030
def test_ok_bitstring_size_restriction_no_max(self) -> None:
9201031
@asn1.sequence
9211032
@_comparable_dataclass

0 commit comments

Comments
 (0)