Skip to content

Commit c41460d

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

File tree

4 files changed

+119
-5
lines changed

4 files changed

+119
-5
lines changed

src/cryptography/hazmat/asn1/asn1.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,18 @@ def _normalize_field_type(
108108
if annotation.size is not None and (
109109
get_type_origin(field_type) is not builtins.list
110110
and field_type
111-
not in (builtins.bytes, builtins.str, BitString, PrintableString)
111+
not in (
112+
builtins.bytes,
113+
builtins.str,
114+
BitString,
115+
IA5String,
116+
PrintableString,
117+
)
112118
):
113119
raise TypeError(
114120
f"field {field_name} has a SIZE annotation, but SIZE annotations "
115121
f"are only supported for fields of types: [SEQUENCE OF, "
116-
"BIT STRING, OCTET STRING, UTF8String, PrintableString]"
122+
"BIT STRING, OCTET STRING, UTF8String, PrintableString, IA5String]"
117123
)
118124

119125
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
@@ -83,9 +83,10 @@ fn decode_printable_string<'a>(
8383
fn decode_ia5_string<'a>(
8484
py: pyo3::Python<'a>,
8585
parser: &mut Parser<'a>,
86-
encoding: &Option<pyo3::Py<Encoding>>,
86+
annotation: &Annotation,
8787
) -> ParseResult<pyo3::Bound<'a, IA5String>> {
88-
let value = read_value::<asn1::IA5String<'a>>(parser, encoding)?.as_str();
88+
let value = read_value::<asn1::IA5String<'a>>(parser, &annotation.encoding)?.as_str();
89+
check_size_constraint(&annotation.size, value.len(), "IA5String")?;
8990
let inner = pyo3::types::PyString::new(py, value).unbind();
9091
Ok(pyo3::Bound::new(py, IA5String { inner })?)
9192
}
@@ -221,7 +222,7 @@ pub(crate) fn decode_annotated_type<'a>(
221222
Type::PyBytes() => decode_pybytes(py, parser, annotation)?.into_any(),
222223
Type::PyStr() => decode_pystr(py, parser, annotation)?.into_any(),
223224
Type::PrintableString() => decode_printable_string(py, parser, annotation)?.into_any(),
224-
Type::IA5String() => decode_ia5_string(py, parser, encoding)?.into_any(),
225+
Type::IA5String() => decode_ia5_string(py, parser, annotation)?.into_any(),
225226
Type::UtcTime() => decode_utc_time(py, parser, encoding)?.into_any(),
226227
Type::GeneralizedTime() => decode_generalized_time(py, parser, encoding)?.into_any(),
227228
Type::BitString() => decode_bitstring(py, parser, annotation)?.into_any(),

src/rust/src/declarative_asn1/encode.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ impl asn1::Asn1Writable for AnnotatedTypeObject<'_> {
162162
.inner
163163
.to_cow(py)
164164
.map_err(|_| asn1::WriteError::AllocationError)?;
165+
check_size_constraint(&annotation.size, inner_str.len(), "IA5String")
166+
.map_err(|_| asn1::WriteError::AllocationError)?;
165167
let ia5_string: asn1::IA5String<'_> =
166168
asn1::IA5String::new(&inner_str).ok_or(asn1::WriteError::AllocationError)?;
167169
write_value(writer, &ia5_string, encoding)

tests/hazmat/asn1/test_serialization.py

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,6 +1027,111 @@ class Example:
10271027
):
10281028
asn1.encode_der(Example(a=asn1.PrintableString("abcd")))
10291029

1030+
def test_ok_ia5string_size_restriction(self) -> None:
1031+
@asn1.sequence
1032+
@_comparable_dataclass
1033+
class Example:
1034+
a: Annotated[asn1.IA5String, asn1.Size(min=1, max=4)]
1035+
1036+
assert_roundtrips(
1037+
[
1038+
(
1039+
Example(a=asn1.IA5String("abcd")),
1040+
b"\x30\x06\x16\x04abcd",
1041+
)
1042+
]
1043+
)
1044+
1045+
def test_ok_ia5string_size_restriction_no_max(self) -> None:
1046+
@asn1.sequence
1047+
@_comparable_dataclass
1048+
class Example:
1049+
a: Annotated[asn1.IA5String, asn1.Size(min=1, max=None)]
1050+
1051+
assert_roundtrips(
1052+
[
1053+
(
1054+
Example(a=asn1.IA5String("abcd")),
1055+
b"\x30\x06\x16\x04abcd",
1056+
)
1057+
]
1058+
)
1059+
1060+
def test_ok_ia5string_size_restriction_exact(self) -> None:
1061+
@asn1.sequence
1062+
@_comparable_dataclass
1063+
class Example:
1064+
a: Annotated[asn1.IA5String, asn1.Size.exact(4)]
1065+
1066+
assert_roundtrips(
1067+
[
1068+
(
1069+
Example(a=asn1.IA5String("abcd")),
1070+
b"\x30\x06\x16\x04abcd",
1071+
)
1072+
]
1073+
)
1074+
1075+
def test_fail_ia5string_size_too_big(self) -> None:
1076+
@asn1.sequence
1077+
@_comparable_dataclass
1078+
class Example:
1079+
a: Annotated[asn1.IA5String, asn1.Size(min=1, max=2)]
1080+
1081+
with pytest.raises(
1082+
ValueError,
1083+
match=re.escape("IA5String has size 4, expected size in [1, 2]"),
1084+
):
1085+
asn1.decode_der(
1086+
Example,
1087+
b"\x30\x06\x16\x04abcd",
1088+
)
1089+
1090+
with pytest.raises(
1091+
ValueError,
1092+
):
1093+
asn1.encode_der(Example(a=asn1.IA5String("abcd")))
1094+
1095+
def test_fail_ia5string_size_too_small(self) -> None:
1096+
@asn1.sequence
1097+
@_comparable_dataclass
1098+
class Example:
1099+
a: Annotated[asn1.IA5String, asn1.Size(min=5, max=6)]
1100+
1101+
with pytest.raises(
1102+
ValueError,
1103+
match=re.escape("IA5String has size 4, expected size in [5, 6]"),
1104+
):
1105+
asn1.decode_der(
1106+
Example,
1107+
b"\x30\x06\x16\x04abcd",
1108+
)
1109+
1110+
with pytest.raises(
1111+
ValueError,
1112+
):
1113+
asn1.encode_der(Example(a=asn1.IA5String("abcd")))
1114+
1115+
def test_fail_ia5string_size_not_exact(self) -> None:
1116+
@asn1.sequence
1117+
@_comparable_dataclass
1118+
class Example:
1119+
a: Annotated[asn1.IA5String, asn1.Size.exact(5)]
1120+
1121+
with pytest.raises(
1122+
ValueError,
1123+
match=re.escape("IA5String has size 4, expected size in [5, 5]"),
1124+
):
1125+
asn1.decode_der(
1126+
Example,
1127+
b"\x30\x06\x16\x04abcd",
1128+
)
1129+
1130+
with pytest.raises(
1131+
ValueError,
1132+
):
1133+
asn1.encode_der(Example(a=asn1.IA5String("abcd")))
1134+
10301135
def test_ok_bitstring_size_restriction_no_max(self) -> None:
10311136
@asn1.sequence
10321137
@_comparable_dataclass

0 commit comments

Comments
 (0)