Skip to content

Commit feb56ca

Browse files
authored
der: conversions between heapless:Vec<u8> and OctetStringRef (#1735)
`heapless::Vec` is useful in `derive(Sequence)` fields in `no_std` environments
1 parent 937e611 commit feb56ca

File tree

4 files changed

+189
-79
lines changed

4 files changed

+189
-79
lines changed

der/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ flagset = { version = "0.4.6", optional = true }
2525
pem-rfc7468 = { version = "1.0.0-rc.1", optional = true, features = ["alloc"] }
2626
time = { version = "0.3.4", optional = true, default-features = false }
2727
zeroize = { version = "1.8", optional = true, default-features = false }
28+
heapless = { version = "0.8", optional = true, default-features = false }
2829

2930
[dev-dependencies]
3031
hex-literal = "1"

der/src/asn1/octet_string.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,27 @@ impl<'a, const N: usize> TryFrom<OctetStringRef<'a>> for [u8; N] {
132132
}
133133
}
134134

135+
#[cfg(feature = "heapless")]
136+
impl<'a, const N: usize> TryFrom<OctetStringRef<'a>> for heapless::Vec<u8, N> {
137+
type Error = Error;
138+
139+
fn try_from(octet_string: OctetStringRef<'a>) -> Result<Self, Self::Error> {
140+
octet_string
141+
.as_bytes()
142+
.try_into()
143+
.map_err(|_| Tag::OctetString.length_error())
144+
}
145+
}
146+
147+
#[cfg(feature = "heapless")]
148+
impl<'a, const N: usize> TryFrom<&'a heapless::Vec<u8, N>> for OctetStringRef<'a> {
149+
type Error = Error;
150+
151+
fn try_from(byte_vec: &'a heapless::Vec<u8, N>) -> Result<Self, Error> {
152+
OctetStringRef::new(byte_vec)
153+
}
154+
}
155+
135156
#[cfg(feature = "alloc")]
136157
pub use self::allocating::OctetString;
137158

der/tests/derive.rs

Lines changed: 0 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -568,85 +568,6 @@ mod sequence {
568568
assert_eq!(obj, obj_decoded);
569569
}
570570

571-
#[derive(Sequence, Default, Eq, PartialEq, Debug)]
572-
#[asn1(tag_mode = "IMPLICIT")]
573-
pub struct TypeCheckArraysSequenceFieldAttributeCombinations {
574-
#[asn1(type = "OCTET STRING", deref = "true")]
575-
pub array_bytes: [u8; 2],
576-
577-
#[asn1(type = "BIT STRING", deref = "true")]
578-
pub array_bits: [u8; 2],
579-
580-
#[asn1(type = "OCTET STRING", context_specific = "0", deref = "true")]
581-
pub array_implicit_bytes: [u8; 2],
582-
583-
#[asn1(type = "BIT STRING", context_specific = "1", deref = "true")]
584-
pub array_implicit_bits: [u8; 2],
585-
586-
#[asn1(
587-
type = "OCTET STRING",
588-
context_specific = "2",
589-
tag_mode = "EXPLICIT",
590-
deref = "true"
591-
)]
592-
pub array_explicit_bytes: [u8; 2],
593-
594-
#[asn1(
595-
type = "BIT STRING",
596-
context_specific = "3",
597-
tag_mode = "EXPLICIT",
598-
deref = "true"
599-
)]
600-
pub array_explicit_bits: [u8; 2],
601-
602-
#[asn1(type = "BIT STRING", context_specific = "4", optional = "true")]
603-
pub array_optional_implicit_bits: Option<[u8; 2]>,
604-
605-
#[asn1(type = "OCTET STRING", context_specific = "5", optional = "true")]
606-
pub array_optional_implicit_bytes: Option<[u8; 2]>,
607-
608-
#[asn1(
609-
type = "BIT STRING",
610-
context_specific = "6",
611-
optional = "true",
612-
tag_mode = "EXPLICIT"
613-
)]
614-
pub array_optional_explicit_bits: Option<[u8; 2]>,
615-
616-
#[asn1(
617-
type = "OCTET STRING",
618-
context_specific = "7",
619-
optional = "true",
620-
tag_mode = "EXPLICIT"
621-
)]
622-
pub array_optional_explicit_bytes: Option<[u8; 2]>,
623-
}
624-
625-
#[test]
626-
fn type_combinations_arrays_instance() {
627-
let obj = TypeCheckArraysSequenceFieldAttributeCombinations {
628-
array_bytes: [0xAA, 0xBB],
629-
array_bits: [0xCC, 0xDD],
630-
631-
array_implicit_bytes: [0, 1],
632-
array_implicit_bits: [2, 3],
633-
634-
array_explicit_bytes: [4, 5],
635-
array_explicit_bits: [6, 7],
636-
637-
array_optional_implicit_bits: Some([8, 9]),
638-
array_optional_implicit_bytes: Some([10, 11]),
639-
640-
array_optional_explicit_bits: Some([12, 13]),
641-
array_optional_explicit_bytes: Some([14, 15]),
642-
};
643-
644-
let der_encoded = obj.to_der().unwrap();
645-
let obj_decoded =
646-
TypeCheckArraysSequenceFieldAttributeCombinations::from_der(&der_encoded).unwrap();
647-
assert_eq!(obj, obj_decoded);
648-
}
649-
650571
#[derive(Sequence)]
651572
#[asn1(error = CustomError)]
652573
pub struct TypeWithCustomError {

der/tests/derive_no_alloc.rs

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
//! Tests for custom derive support. Without alloc feature.
2+
//!
3+
//! # Debugging with `cargo expand`
4+
//!
5+
//! To expand the Rust code generated by the proc macro when debugging
6+
//! issues related to these tests, run:
7+
//!
8+
//! $ cargo expand --test derive_heapless --all-features
9+
10+
#![cfg(feature = "derive")]
11+
// TODO: fix needless_question_mark in the derive crate
12+
#![allow(clippy::needless_question_mark)]
13+
14+
/// Custom derive test cases for the `Sequence` macro, without alloc.
15+
mod sequence {
16+
use der::Decode;
17+
use der::Encode;
18+
use der::Sequence;
19+
20+
#[derive(Sequence, Default, Eq, PartialEq, Debug)]
21+
#[asn1(tag_mode = "IMPLICIT")]
22+
pub struct TypeCheckArraysSequenceFieldAttributeCombinations {
23+
#[asn1(type = "OCTET STRING", deref = "true")]
24+
pub array_bytes: [u8; 2],
25+
26+
#[asn1(type = "BIT STRING", deref = "true")]
27+
pub array_bits: [u8; 2],
28+
29+
#[asn1(type = "OCTET STRING", context_specific = "0", deref = "true")]
30+
pub array_implicit_bytes: [u8; 2],
31+
32+
#[asn1(type = "BIT STRING", context_specific = "1", deref = "true")]
33+
pub array_implicit_bits: [u8; 2],
34+
35+
#[asn1(
36+
type = "OCTET STRING",
37+
context_specific = "2",
38+
tag_mode = "EXPLICIT",
39+
deref = "true"
40+
)]
41+
pub array_explicit_bytes: [u8; 2],
42+
43+
#[asn1(
44+
type = "BIT STRING",
45+
context_specific = "3",
46+
tag_mode = "EXPLICIT",
47+
deref = "true"
48+
)]
49+
pub array_explicit_bits: [u8; 2],
50+
51+
#[asn1(type = "BIT STRING", context_specific = "4", optional = "true")]
52+
pub array_optional_implicit_bits: Option<[u8; 2]>,
53+
54+
#[asn1(type = "OCTET STRING", context_specific = "5", optional = "true")]
55+
pub array_optional_implicit_bytes: Option<[u8; 2]>,
56+
57+
#[asn1(
58+
type = "BIT STRING",
59+
context_specific = "6",
60+
optional = "true",
61+
tag_mode = "EXPLICIT"
62+
)]
63+
pub array_optional_explicit_bits: Option<[u8; 2]>,
64+
65+
#[asn1(
66+
type = "OCTET STRING",
67+
context_specific = "7",
68+
optional = "true",
69+
tag_mode = "EXPLICIT"
70+
)]
71+
pub array_optional_explicit_bytes: Option<[u8; 2]>,
72+
}
73+
74+
#[test]
75+
fn type_combinations_arrays_instance() {
76+
let mut buf = [0u8; 100];
77+
let obj = TypeCheckArraysSequenceFieldAttributeCombinations {
78+
array_bytes: [0xAA, 0xBB],
79+
array_bits: [0xCC, 0xDD],
80+
81+
array_implicit_bytes: [0, 1],
82+
array_implicit_bits: [2, 3],
83+
84+
array_explicit_bytes: [4, 5],
85+
array_explicit_bits: [6, 7],
86+
87+
array_optional_implicit_bits: Some([8, 9]),
88+
array_optional_implicit_bytes: Some([10, 11]),
89+
90+
array_optional_explicit_bits: Some([12, 13]),
91+
array_optional_explicit_bytes: Some([14, 15]),
92+
};
93+
94+
let der_encoded = obj.encode_to_slice(&mut buf).unwrap();
95+
let obj_decoded =
96+
TypeCheckArraysSequenceFieldAttributeCombinations::from_der(der_encoded).unwrap();
97+
assert_eq!(obj, obj_decoded);
98+
}
99+
}
100+
/// Custom derive test cases for the `Sequence` macro with heapless crate.
101+
#[cfg(all(feature = "oid", feature = "heapless"))]
102+
mod sequence_heapless_vec {
103+
use der::Decode;
104+
use der::Encode;
105+
use der::Sequence;
106+
use der::asn1::ObjectIdentifier;
107+
use hex_literal::hex;
108+
109+
/// EU Tachograph Gen 2 certificate public key
110+
#[derive(Sequence, Debug, Clone)]
111+
#[asn1(tag_mode = "IMPLICIT")]
112+
pub struct G2CertificatePublicKey {
113+
pub domain_parameters: ObjectIdentifier,
114+
115+
/// Public point is '04 xx .. xx yy .. yy'
116+
///
117+
/// Maximum: 1 + 2 * 66 bytes for Nist521
118+
#[asn1(context_specific = "6", type = "OCTET STRING", deref = "true")]
119+
pub public_point: heapless::Vec<u8, 133>,
120+
}
121+
122+
static ROMANIA_PUBLIC_KEY: [u8; 80] = hex!(
123+
"
124+
30 4E 06 09 2B 24 03 03 02 08 01 01 07 86 41
125+
04 86 23 90 68 B8 08 1F 5E 6B 49 EB 54 F7 8E 0D
126+
FB E7 1F 85 26 15 60 73 7C 24 B0 10 24 F9 2A 02
127+
03 67 80 3E 17 E9 F2 6E D5 A5 4E 25 F9 B5 46 B1
128+
90 3C DD 76 47 DB 1D 8E E0 D9 86 D0 64 D8 3C DD
129+
7F"
130+
);
131+
132+
pub const BRAINPOOL_P_256_R_1: ObjectIdentifier =
133+
ObjectIdentifier::new_unwrap("1.3.36.3.3.2.8.1.1.7");
134+
135+
#[test]
136+
fn heapless_vec_decode_encode() {
137+
let public_key = G2CertificatePublicKey::from_der(&ROMANIA_PUBLIC_KEY).unwrap();
138+
139+
assert_eq!(public_key.domain_parameters, BRAINPOOL_P_256_R_1);
140+
assert_eq!(public_key.public_point.len(), 1 + 2 * (256 / 8));
141+
142+
let mut buf = [0u8; 80];
143+
let pk_encoded = public_key.encode_to_slice(&mut buf).unwrap();
144+
145+
assert_eq!(pk_encoded, ROMANIA_PUBLIC_KEY);
146+
}
147+
148+
#[derive(Sequence, Debug, Clone)]
149+
pub struct HeaplessTypeCheck {
150+
#[asn1(type = "OCTET STRING", deref = "true")]
151+
pub octet_string_heapless: heapless::Vec<u8, 16>,
152+
153+
#[asn1(type = "OCTET STRING", deref = "true", tag_mode = "IMPLICIT")]
154+
pub octet_string_heapless_implicit: heapless::Vec<u8, 16>,
155+
156+
#[asn1(context_specific = "0", type = "OCTET STRING", optional = "true")]
157+
pub opt_octet_string_heapless: Option<heapless::Vec<u8, 16>>,
158+
159+
#[asn1(
160+
context_specific = "1",
161+
type = "OCTET STRING",
162+
optional = "true",
163+
tag_mode = "IMPLICIT"
164+
)]
165+
pub opt_octet_string_heapless_implicit: Option<heapless::Vec<u8, 16>>,
166+
}
167+
}

0 commit comments

Comments
 (0)