Skip to main content

structured_pkix/algorithm/dstu4145/
parameters.rs

1//! Original `DSTU4145Params`, read without group arithmetic or inheritance.
2
3use super::{Basis, ByteOrder, Octets, Profile, identify};
4use crate::algorithm::{AlgorithmIdentifier, Producer};
5use crate::error::Located;
6use crate::{Error, ErrorCode, Result};
7use structured_asn1::memory::{Execution, Work};
8use structured_asn1::{Element, Encoding, Integer, Oid, ScopedReader, Span, Tag, universal};
9
10const FIELD: &str = "dstu4145.parameters";
11
12/// Original nonnegative ASN.1 INTEGER with its complete encoded range.
13#[derive(Debug, Clone, Copy)]
14pub struct IntegerField<'a> {
15    span: Span,
16    value: Integer<'a>,
17}
18
19impl<'a> IntegerField<'a> {
20    /// Original INTEGER value, not a narrowed backend scalar.
21    #[must_use]
22    pub const fn value(self) -> Integer<'a> {
23        self.value
24    }
25
26    /// Original complete INTEGER range, including its tag and header.
27    #[must_use]
28    pub const fn span(self) -> Span {
29        self.span
30    }
31}
32
33/// Field representation, not a primality or irreducibility assertion.
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum Reduction {
36    /// Polynomial x^m + x^k + 1.
37    Trinomial(u32),
38    /// Polynomial x^m + x^k + x^j + x^l + 1, with k < j < l.
39    Pentanomial {
40        /// Smallest nonzero reduction exponent.
41        k: u32,
42        /// Middle reduction exponent.
43        j: u32,
44        /// Largest reduction exponent, strictly below m.
45        l: u32,
46    },
47    /// Omitted reduction polynomial for an optimal normal basis.
48    OptimalNormal,
49}
50
51/// A written named-curve OID, including unknown names.
52#[derive(Debug, Clone, Copy)]
53pub struct NamedCurve<'a> {
54    element: Element<'a>,
55    identifier: Oid<'a>,
56    known: Option<(Basis, u32)>,
57}
58
59impl<'a> NamedCurve<'a> {
60    /// Exact written identity, never inferred from point width.
61    #[must_use]
62    pub const fn identifier(self) -> Oid<'a> {
63        self.identifier
64    }
65
66    /// Original OID element and its source coordinates.
67    #[must_use]
68    pub const fn element(self) -> Element<'a> {
69        self.element
70    }
71
72    /// Known field basis and degree, or None for an unsupported curve name.
73    /// No mathematical parameters are synthesized for unknown names.
74    #[must_use]
75    pub const fn known(self) -> Option<(Basis, u32)> {
76        self.known
77    }
78}
79
80/// Written explicit curve components. No point, order or field math is done.
81#[derive(Debug, Clone, Copy)]
82pub struct ExplicitCurve<'a> {
83    element: Element<'a>,
84    degree: IntegerField<'a>,
85    degree_value: u32,
86    field: Span,
87    reduction: Reduction,
88    a: IntegerField<'a>,
89    b: Octets<'a>,
90    order: IntegerField<'a>,
91    base_point: Octets<'a>,
92}
93
94impl<'a> ExplicitCurve<'a> {
95    /// Whole original `ECBinary` SEQUENCE.
96    #[must_use]
97    pub const fn element(self) -> Element<'a> {
98        self.element
99    }
100    /// Original complete `BinaryField` range, including reduction exponents.
101    #[must_use]
102    pub const fn field_span(self) -> Span {
103        self.field
104    }
105    /// Original degree INTEGER.
106    #[must_use]
107    pub const fn degree(self) -> IntegerField<'a> {
108        self.degree
109    }
110    /// Structural field degree, not a supported-backend assertion.
111    #[must_use]
112    pub const fn degree_value(self) -> u32 {
113        self.degree_value
114    }
115    /// Polynomial exponents or optimal-normal-basis omission.
116    #[must_use]
117    pub const fn reduction(self) -> Reduction {
118        self.reduction
119    }
120    /// Coefficient A, constrained to zero or one.
121    #[must_use]
122    pub const fn a(self) -> IntegerField<'a> {
123        self.a
124    }
125    /// Coefficient B in the selected byte order, still unvalidated mathematically.
126    #[must_use]
127    pub const fn b(self) -> Octets<'a> {
128        self.b
129    }
130    /// Positive subgroup-order candidate, not a primality assertion.
131    #[must_use]
132    pub const fn order(self) -> IntegerField<'a> {
133        self.order
134    }
135    /// Compressed original base point; no decompression is attempted.
136    #[must_use]
137    pub const fn base_point(self) -> Octets<'a> {
138        self.base_point
139    }
140}
141
142/// The actual named or explicit CHOICE, without a guessed fallback.
143#[derive(Debug, Clone, Copy)]
144pub enum Curve<'a> {
145    /// A named curve, possibly unknown to the bounded metadata table.
146    Named(NamedCurve<'a>),
147    /// Explicit original components.
148    Explicit(ExplicitCurve<'a>),
149}
150
151impl Curve<'_> {
152    /// Field degree when written explicitly or known from the exact curve OID.
153    #[must_use]
154    pub const fn degree(&self) -> Option<u32> {
155        match self {
156            Self::Named(curve) => match curve.known {
157                Some((_, degree)) => Some(degree),
158                None => None,
159            },
160            Self::Explicit(curve) => Some(curve.degree_value),
161        }
162    }
163}
164
165/// Borrowed original `DSTU4145Params`. Storage and arithmetic belong to callers.
166#[derive(Debug, Clone, Copy)]
167pub struct Parameters<'a> {
168    element: Element<'a>,
169    curve: Curve<'a>,
170    dke: Option<Element<'a>>,
171    producer: Producer<'a>,
172}
173
174impl<'a> Parameters<'a> {
175    /// Retains the borrowed producer for deferred, explicitly funded backends.
176    /// This is a resource origin, not a parameter or document identity.
177    #[must_use]
178    pub const fn execution_origin(self) -> Producer<'a> {
179        self.producer
180    }
181    /// Whole original parameter SEQUENCE, without canonicalization.
182    #[must_use]
183    pub const fn element(self) -> Element<'a> {
184        self.element
185    }
186    /// Original named or explicit curve.
187    #[must_use]
188    pub const fn curve(self) -> Curve<'a> {
189        self.curve
190    }
191    /// Written packed 64-octet DKE, or None for normative DKE number one.
192    /// Absence never inherits an authority's DKE. Provider selection of the
193    /// default is separate from parsing (§§3.11.1 and 3.12 of the profile).
194    #[must_use]
195    pub const fn dke(self) -> Option<Element<'a>> {
196        self.dke
197    }
198    /// Funds deferred interpretation from the original and current ceilings.
199    #[must_use]
200    pub fn execution_in<'q, W: Work>(&self, current: &'q W) -> Execution<'q>
201    where
202        'a: 'q,
203    {
204        self.producer.execution_in(current)
205    }
206}
207
208/// Reads key-role parameters under the producing parser and current Work.
209///
210/// Returns None for actual absence, not NULL and not inherited EC parameters.
211/// The pinned profile §§3.10–3.12 requires DER and a 64-octet packed DKE.
212/// Unknown named curves remain inspectable with `known() == None`; neither
213/// that fact nor readable explicit components permits a provider operation.
214/// <https://zakon.rada.gov.ua/laws/show/z1398-12>
215///
216/// # Errors
217/// Another algorithm, malformed/extra fields, unsupported explicit version,
218/// written DER DEFAULT, or the original located resource/processing failure.
219pub fn read_parameters<'a, W: Work>(
220    identifier: AlgorithmIdentifier<'a>,
221    current: &W,
222) -> Result<Option<Parameters<'a>>> {
223    let profile = identify(identifier, current)?
224        .ok_or_else(|| Error::new(ErrorCode::UnexpectedField, FIELD, identifier.offset()))?;
225    read_known(identifier, profile, current)
226}
227
228pub(crate) fn read_known<'a, W: Work>(
229    identifier: AlgorithmIdentifier<'a>,
230    profile: Profile,
231    current: &W,
232) -> Result<Option<Parameters<'a>>> {
233    let Some(source) = identifier.parameters_nested() else {
234        return Ok(None);
235    };
236    let (reader, element) = source.open(current, FIELD)?;
237    let value = if reader.encoding().is_der() {
238        fields(&reader, element, profile, identifier.execution_origin())?
239    } else {
240        let mut strict = reader.reread(&element, Encoding::Der).field(FIELD)?;
241        let element = strict
242            .next_element()
243            .field(FIELD)?
244            .ok_or_else(|| Error::new(ErrorCode::MissingField, FIELD, strict.offset()))?;
245        fields(&strict, element, profile, identifier.execution_origin())?
246    };
247    Ok(Some(value))
248}
249
250fn fields<'a>(
251    reader: &ScopedReader<'a, '_>,
252    element: Element<'a>,
253    profile: Profile,
254    producer: Producer<'a>,
255) -> Result<Parameters<'a>> {
256    require_tag(element, Tag::constructed(universal::SEQUENCE))?;
257    let mut inside = reader.enter(&element).field(FIELD)?;
258    let first = next(&mut inside)?;
259    let curve = if first.tag() == Tag::primitive(universal::OID) {
260        let identifier = inside.oid(&first).field(FIELD)?;
261        inside
262            .charge(identifier.as_bytes().len() as u64 + 1)
263            .field(FIELD)?;
264        let known = named(identifier);
265        if let Some((basis, _)) = known
266            && basis != profile.basis()
267        {
268            return Err(Error::new(
269                ErrorCode::MalformedValue,
270                FIELD,
271                first.span().start,
272            ));
273        }
274        Curve::Named(NamedCurve {
275            element: first,
276            identifier,
277            known,
278        })
279    } else {
280        Curve::Explicit(explicit(&inside, first, profile)?)
281    };
282    let dke = inside.next_element().field(FIELD)?;
283    if let Some(dke) = dke {
284        require_tag(dke, Tag::primitive(universal::OCTET_STRING))?;
285        if dke.contents().len() != 64 {
286            return Err(Error::new(
287                ErrorCode::MalformedValue,
288                FIELD,
289                dke.span().start,
290            ));
291        }
292    }
293    exhausted(&inside)?;
294    Ok(Parameters {
295        element,
296        curve,
297        dke,
298        producer,
299    })
300}
301
302fn explicit<'a>(
303    reader: &ScopedReader<'a, '_>,
304    element: Element<'a>,
305    profile: Profile,
306) -> Result<ExplicitCurve<'a>> {
307    require_tag(element, Tag::constructed(universal::SEQUENCE))?;
308    let mut inside = reader.enter(&element).field(FIELD)?;
309    let field = next(&mut inside)?;
310    if field.tag() == Tag::context(true, 0) {
311        let mut version = inside.enter(&field).field(FIELD)?;
312        let value = integer(&mut version)?;
313        exhausted(&version)?;
314        let value = number(&version, value)?;
315        // X.690 §11.5: DEFAULT zero must not be explicitly written in DER.
316        // https://www.itu.int/rec/T-REC-X.690-202102-I/en
317        let code = if value == 0 {
318            ErrorCode::DefaultWritten
319        } else {
320            ErrorCode::UnsupportedVersion
321        };
322        return Err(Error::new(code, FIELD, field.span().start));
323    }
324    require_tag(field, Tag::constructed(universal::SEQUENCE))?;
325    let mut binary = inside.enter(&field).field(FIELD)?;
326    let degree = integer(&mut binary)?;
327    let degree_value = number(&binary, degree)?;
328    if degree_value == 0 {
329        return Err(Error::new(
330            ErrorCode::MalformedValue,
331            FIELD,
332            degree.span.start,
333        ));
334    }
335    let reduction = if profile.basis() == Basis::OptimalNormal {
336        exhausted(&binary)?;
337        Reduction::OptimalNormal
338    } else {
339        let written = next(&mut binary)?;
340        let reduction = if written.tag() == Tag::primitive(universal::INTEGER) {
341            let k = integer_element(&binary, written)?;
342            let k = number(&binary, k)?;
343            if k == 0 || k >= degree_value {
344                return Err(Error::new(
345                    ErrorCode::MalformedValue,
346                    FIELD,
347                    written.span().start,
348                ));
349            }
350            Reduction::Trinomial(k)
351        } else {
352            require_tag(written, Tag::constructed(universal::SEQUENCE))?;
353            let mut terms = binary.enter(&written).field(FIELD)?;
354            let k = integer(&mut terms)?;
355            let k = number(&terms, k)?;
356            let j = integer(&mut terms)?;
357            let j = number(&terms, j)?;
358            let l = integer(&mut terms)?;
359            let l = number(&terms, l)?;
360            exhausted(&terms)?;
361            if k == 0 || k >= j || j >= l || l >= degree_value {
362                return Err(Error::new(
363                    ErrorCode::MalformedValue,
364                    FIELD,
365                    written.span().start,
366                ));
367            }
368            Reduction::Pentanomial { k, j, l }
369        };
370        exhausted(&binary)?;
371        reduction
372    };
373    let a = integer(&mut inside)?;
374    if number(&inside, a)? > 1 {
375        return Err(Error::new(ErrorCode::MalformedValue, FIELD, a.span.start));
376    }
377    let b_element = next(&mut inside)?;
378    let b = field_octets(&inside, b_element, degree_value, profile.byte_order())?;
379    let order = integer(&mut inside)?;
380    inside.charge(1).field(FIELD)?;
381    if order.value.as_bytes() == [0] {
382        return Err(Error::new(
383            ErrorCode::MalformedValue,
384            FIELD,
385            order.span.start,
386        ));
387    }
388    let point_element = next(&mut inside)?;
389    let base_point = field_octets(&inside, point_element, degree_value, profile.byte_order())?;
390    exhausted(&inside)?;
391    Ok(ExplicitCurve {
392        element,
393        degree,
394        degree_value,
395        field: field.span(),
396        reduction,
397        a,
398        b,
399        order,
400        base_point,
401    })
402}
403
404pub(crate) fn field_octets<'a>(
405    reader: &ScopedReader<'a, '_>,
406    element: Element<'a>,
407    degree: u32,
408    byte_order: ByteOrder,
409) -> Result<Octets<'a>> {
410    require_tag(element, Tag::primitive(universal::OCTET_STRING))?;
411    let bytes = element.contents();
412    reader.charge(1).field(FIELD)?;
413    if degree == 0 || bytes.len() as u64 != u64::from(degree).div_ceil(8) {
414        return Err(Error::new(
415            ErrorCode::MalformedValue,
416            FIELD,
417            element.span().start,
418        ));
419    }
420    let unused = (8 - degree % 8) % 8;
421    let top = match byte_order {
422        ByteOrder::LittleEndian => bytes[bytes.len() - 1],
423        ByteOrder::BigEndian => bytes[0],
424    };
425    if unused != 0 && top >> (8 - unused) != 0 {
426        return Err(Error::new(
427            ErrorCode::MalformedValue,
428            FIELD,
429            element.contents_span().start,
430        ));
431    }
432    Ok(Octets::new(
433        bytes,
434        byte_order,
435        element.contents_span().start,
436    ))
437}
438
439fn named(identifier: Oid<'_>) -> Option<(Basis, u32)> {
440    let (basis, index) = match identifier.as_bytes() {
441        [0x2a, 0x86, 0x24, 2, 1, 1, 1, 1, 3, 1, basis, 2, index]
442        | [
443            0x2a,
444            0x86,
445            0x24,
446            2,
447            1,
448            1,
449            1,
450            1,
451            3,
452            6,
453            1..=3,
454            basis,
455            2,
456            index,
457        ] => (*basis, *index),
458        _ => return None,
459    };
460    // Pinned OID table, named polynomial and optimal-normal-basis branches.
461    // https://zakon.rada.gov.ua/laws/show/z1399-12
462    let (basis, degrees): (_, &[u32]) = match basis {
463        1 => (
464            Basis::Polynomial,
465            &[163, 167, 173, 179, 191, 233, 257, 307, 367, 431],
466        ),
467        2 => (Basis::OptimalNormal, &[173, 179, 191, 233, 431]),
468        _ => return None,
469    };
470    degrees
471        .get(usize::from(index))
472        .map(|degree| (basis, *degree))
473}
474
475fn require_tag(element: Element<'_>, tag: Tag) -> Result<()> {
476    if element.tag() != tag {
477        return Err(Error::new(
478            ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag),
479            FIELD,
480            element.span().start,
481        ));
482    }
483    Ok(())
484}
485fn next<'a>(reader: &mut ScopedReader<'a, '_>) -> Result<Element<'a>> {
486    reader
487        .next_element()
488        .field(FIELD)?
489        .ok_or_else(|| Error::new(ErrorCode::MissingField, FIELD, reader.offset()))
490}
491fn exhausted(reader: &ScopedReader<'_, '_>) -> Result<()> {
492    if !reader.is_empty() {
493        return Err(Error::new(ErrorCode::TrailingData, FIELD, reader.offset()));
494    }
495    Ok(())
496}
497fn integer<'a>(reader: &mut ScopedReader<'a, '_>) -> Result<IntegerField<'a>> {
498    let element = next(reader)?;
499    integer_element(reader, element)
500}
501fn integer_element<'a>(
502    reader: &ScopedReader<'a, '_>,
503    element: Element<'a>,
504) -> Result<IntegerField<'a>> {
505    require_tag(element, Tag::primitive(universal::INTEGER))?;
506    let value = reader.integer(&element).field(FIELD)?;
507    reader.charge(1).field(FIELD)?;
508    if value.is_negative() {
509        return Err(Error::new(
510            ErrorCode::MalformedValue,
511            FIELD,
512            element.span().start,
513        ));
514    }
515    Ok(IntegerField {
516        span: element.span(),
517        value,
518    })
519}
520fn number(reader: &ScopedReader<'_, '_>, value: IntegerField<'_>) -> Result<u32> {
521    reader
522        .charge(value.value.as_bytes().len() as u64)
523        .field(FIELD)?;
524    value
525        .value
526        .as_u32()
527        .ok_or_else(|| Error::new(ErrorCode::ValueTooLarge, FIELD, value.span.start))
528}