Skip to main content

geonetworking/
validate.rs

1use ecdsa::{
2    elliptic_curve::{point::DecompressPoint, subtle::Choice},
3    signature::Verifier,
4    VerifyingKey,
5};
6use num::Integer;
7use sha2::{Digest, Sha256, Sha384};
8
9use crate::{ieee1609dot2, EncodeError, Packet};
10
11/// Packet validation
12pub trait Validate {
13    /// Checks whether the implementing type is valid.
14    ///
15    /// This method shall run the following checks:
16    ///
17    /// - The signature of a secured packet matches the certificate contained in the IEEE 1609.2 header
18    /// - (*not implemented yet*: The packet conforms to IEEE 1609.2 2016)
19    /// - (*not implemented yet*: The packet conforms to ETSI TS 103 097 V2.1.1)
20    ///
21    /// # Returns
22    ///
23    /// - `Ok(ValidationResult::Success)` if all checks passed successful
24    /// - `Ok(ValidationResult::Failure { reason: String })` if a check failed
25    /// - `Ok(ValidationResult::NotApplicable { info: &'static str })` if no validation checks were run
26    /// - `Err(ValidationError)` if an internal error occured during validation
27    ///
28    /// # Errors
29    /// Returns validation error details when message is invalid
30    fn validate(&self) -> Result<ValidationResult, ValidationError>;
31}
32
33#[derive(Debug, PartialEq)]
34pub enum ValidationResult {
35    Success,
36    Failure { reason: String },
37    NotApplicable { info: &'static str },
38}
39
40#[derive(Debug, PartialEq)]
41pub enum ValidationError {
42    InvalidInput(String),
43    Unsupported(String),
44    ReencodingError(String),
45}
46
47impl From<EncodeError> for ValidationError {
48    fn from(value: EncodeError) -> Self {
49        ValidationError::ReencodingError(value.message().into())
50    }
51}
52
53impl Validate for ieee1609dot2::Ieee1609Dot2Data<'_> {
54    fn validate(&self) -> Result<ValidationResult, ValidationError> {
55        if self.protocol_version != ieee1609dot2::Uint8(3) {
56            return Ok(ValidationResult::Failure {
57                reason: format!(
58                    "Protocol version of IEEE 1609.2 data must be 3. Found {}",
59                    self.protocol_version.0
60                ),
61            });
62        }
63        self.content.validate()
64    }
65}
66
67impl Validate for ieee1609dot2::Ieee1609Dot2Content<'_> {
68    fn validate(&self) -> Result<ValidationResult, ValidationError> {
69        match self {
70            Self::UnsecuredData(_) => todo!(),
71            Self::SignedData(s) => s.validate(),
72            Self::EncryptedData(_) => todo!(),
73            Self::SignedCertificateRequest(_) => todo!(),
74            Self::SignedX509CertificateRequest(_) => todo!(),
75        }
76    }
77}
78
79macro_rules! validate_and_continue {
80    ($candidate:expr) => {
81        match $candidate.validate()? {
82            ValidationResult::Success => (),
83            ValidationResult::NotApplicable { .. } => (),
84            failure => return Ok(failure),
85        }
86    };
87}
88
89impl Validate for Packet<'_> {
90    fn validate(&self) -> Result<ValidationResult, ValidationError> {
91        match self {
92            Self::Unsecured { .. } => Ok(ValidationResult::NotApplicable {
93                info: "Unsecured GeoNetworking packets are not validated.",
94            }),
95            Self::Secured { secured, .. } => secured.validate(),
96        }
97    }
98}
99
100impl Validate for ieee1609dot2::SignedData<'_> {
101    fn validate(&self) -> Result<ValidationResult, ValidationError> {
102        let data = self.tbs_data.raw;
103
104        validate_and_continue!(&self.tbs_data.header_info);
105        validate_and_continue!(&self.signer);
106
107        let (verifying_key, encoded_certificate) = match &self.signer {
108            ieee1609dot2::SignerIdentifier::Certificate(c) => {
109                let certificate = c.0.first().ok_or(ValidationError::InvalidInput(
110                    "Certificate list is empty!".into(),
111                ))?;
112                (&certificate.to_be_signed.verify_key_indicator, certificate.raw)
113            }
114            ieee1609dot2::SignerIdentifier::Digest(_) => {
115                // TODO: Support digest lookup
116                return Err(ValidationError::Unsupported("Certificate retrieval by digest is unsupported!".into()))
117            },
118            ieee1609dot2::SignerIdentifier::RsSelf(()) => {
119                return Ok(ValidationResult::Failure {
120                    reason: "Violates ETSI TS 103 097: Signer Identifier must be of type Digest or Certificate!".into(),
121                })
122            }
123        };
124
125        match (&self.signature, verifying_key) {
126            (
127                ieee1609dot2::Signature::EcdsaNistP256Signature(ieee1609dot2::EcdsaP256Signature {
128                    r_sig,
129                    s_sig,
130                }),
131                ieee1609dot2::VerificationKeyIndicator::VerificationKey(
132                    ieee1609dot2::PublicVerificationKey::EcdsaNistP256(key),
133                ),
134            ) => ecdsa_nist_p256(r_sig, s_sig, key, data, Some(encoded_certificate)),
135            (
136                ieee1609dot2::Signature::EcdsaBrainpoolP256r1Signature(
137                    ieee1609dot2::EcdsaP256Signature { r_sig, s_sig },
138                ),
139                ieee1609dot2::VerificationKeyIndicator::VerificationKey(
140                    ieee1609dot2::PublicVerificationKey::EcdsaBrainpoolP256r1(key),
141                ),
142            ) => ecdsa_brainpool_p256_r1(r_sig, s_sig, key, data, Some(encoded_certificate)),
143            (
144                ieee1609dot2::Signature::EcdsaBrainpoolP384r1Signature(
145                    ieee1609dot2::EcdsaP384Signature { r_sig, s_sig },
146                ),
147                ieee1609dot2::VerificationKeyIndicator::VerificationKey(
148                    ieee1609dot2::PublicVerificationKey::EcdsaBrainpoolP384r1(key),
149                ),
150            ) => ecdsa_brainpool_p384_r1(r_sig, s_sig, key, data, Some(encoded_certificate)),
151            (
152                ieee1609dot2::Signature::EcdsaNistP384Signature(ieee1609dot2::EcdsaP384Signature {
153                    r_sig,
154                    s_sig,
155                }),
156                ieee1609dot2::VerificationKeyIndicator::VerificationKey(
157                    ieee1609dot2::PublicVerificationKey::EcdsaNistP384(key),
158                ),
159            ) => ecdsa_nist_p384(r_sig, s_sig, key, data, Some(encoded_certificate)),
160            (
161                ieee1609dot2::Signature::Sm2Signature(ieee1609dot2::EcsigP256Signature {
162                    r_sig,
163                    s_sig,
164                }),
165                ieee1609dot2::VerificationKeyIndicator::VerificationKey(
166                    ieee1609dot2::PublicVerificationKey::EcsigSm2(key),
167                ),
168            ) => ecdsa_sm2(r_sig, s_sig, key, data, Some(encoded_certificate)),
169            _ => Ok(ValidationResult::Failure {
170                reason: format!(
171                    "Elliptic curve mismatch between signature {:?} and verifying key {:?}",
172                    self.signature, verifying_key
173                ),
174            }),
175        }
176    }
177}
178
179impl Validate for ieee1609dot2::SignerIdentifier<'_> {
180    fn validate(&self) -> Result<ValidationResult, ValidationError> {
181        match self {
182            Self::Digest(_) => Ok(ValidationResult::Success),
183            Self::Certificate(c) if c.0.len() == 1 => Ok(ValidationResult::Success),
184            Self::Certificate(_) => Ok(ValidationResult::Failure {
185                reason: "Exactly one certificate must be included!".into(),
186            }),
187            Self::RsSelf(()) => Ok(ValidationResult::Failure {
188                reason: "Violates ETSI TS 103 097: Signer Identifier must be of type Digest or Certificate!".into(),
189            }),
190        }
191    }
192}
193
194impl Validate for ieee1609dot2::Certificate<'_> {
195    fn validate(&self) -> Result<ValidationResult, ValidationError> {
196        Ok(ValidationResult::Success)
197    }
198}
199
200impl Validate for ieee1609dot2::HeaderInfo<'_> {
201    fn validate(&self) -> Result<ValidationResult, ValidationError> {
202        match (self.expiry_time.as_ref(), self.generation_time.as_ref()) {
203            (Some(exp), Some(gen)) if gen <= exp => {
204                return Ok(ValidationResult::Failure {
205                    reason: "Expiry timestamp is older than generation timestamp.".into(),
206                })
207            }
208            (_, None) => {
209                return Ok(ValidationResult::Failure {
210                    reason: "Generation time must be present!".into(),
211                })
212            }
213            _ => (),
214        }
215        if self.p2pcd_learning_request.is_some() {
216            return Ok(ValidationResult::Failure {
217                reason: "P2PCD Learning Request must be absent!".into(),
218            });
219        }
220        if self.missing_crl_identifier.is_some() {
221            return Ok(ValidationResult::Failure {
222                reason: "Missing CRL Identifier must be absent!".into(),
223            });
224        }
225        Ok(ValidationResult::Success)
226    }
227}
228
229fn sha256(data: &[u8]) -> Vec<u8> {
230    let mut hasher = Sha256::new();
231    hasher.update(data);
232    hasher.finalize().as_slice().to_vec()
233}
234
235fn sha384(data: &[u8]) -> Vec<u8> {
236    let mut hasher = Sha384::new();
237    hasher.update(data);
238    hasher.finalize().as_slice().to_vec()
239}
240
241fn sm3(data: &[u8]) -> Vec<u8> {
242    let mut hasher = sm3::Sm3::new();
243    hasher.update(data);
244    hasher.finalize().as_slice().to_vec()
245}
246
247fn ecdsa_brainpool_p256_r1(
248    r: &ieee1609dot2::EccP256CurvePoint,
249    s: &[u8],
250    curve_point: &ieee1609dot2::EccP256CurvePoint,
251    msg: &[u8],
252    encoded_certificate: Option<&[u8]>,
253) -> Result<ValidationResult, ValidationError> {
254    let r_unwrapped = match r {
255        ieee1609dot2::EccP256CurvePoint::Fill(()) => {
256            return Ok(ValidationResult::Failure {
257                reason: "R value of signature is not given!".into(),
258            })
259        }
260        ieee1609dot2::EccP256CurvePoint::XOnly(x)
261        | ieee1609dot2::EccP256CurvePoint::CompressedY0(x)
262        | ieee1609dot2::EccP256CurvePoint::CompressedY1(x)
263        | ieee1609dot2::EccP256CurvePoint::UncompressedP256(
264            ieee1609dot2::EccP256CurvePointUncompressedP256 { x, .. },
265        ) => x,
266    };
267
268    let signature = ecdsa::Signature::<bp256::BrainpoolP256r1>::from_scalars(
269        bp256::FieldBytes::try_from(*r_unwrapped)
270            .map_err(|err| ValidationError::InvalidInput(format!("Invalid R length: {err:?}")))?,
271        bp256::FieldBytes::try_from(s)
272            .map_err(|err| ValidationError::InvalidInput(format!("Invalid S length: {err:?}")))?,
273    )
274    .unwrap();
275
276    let verifying_key = match curve_point {
277        ieee1609dot2::EccP256CurvePoint::CompressedY0(x) => {
278            let affine = bp256::r1::AffinePoint::decompress(
279                &bp256::FieldBytes::try_from(*x).map_err(|err| {
280                    ValidationError::InvalidInput(format!(
281                        "Invalid verifying key compressed-y0 length: {err:?}"
282                    ))
283                })?,
284                Choice::from(0),
285            )
286            .unwrap();
287            VerifyingKey::from_affine(affine)
288                .map_err(|e| ValidationError::InvalidInput(format!("{e:?}")))
289        }
290        ieee1609dot2::EccP256CurvePoint::CompressedY1(x) => {
291            let affine = bp256::r1::AffinePoint::decompress(
292                &bp256::FieldBytes::try_from(*x).map_err(|err| {
293                    ValidationError::InvalidInput(format!(
294                        "Invalid verifying key compressed-y1 length: {err:?}"
295                    ))
296                })?,
297                Choice::from(1),
298            )
299            .unwrap();
300            VerifyingKey::from_affine(affine)
301                .map_err(|e| ValidationError::InvalidInput(format!("{e:?}")))
302        }
303        ieee1609dot2::EccP256CurvePoint::UncompressedP256(
304            ieee1609dot2::EccP256CurvePointUncompressedP256 { x, y },
305        ) => {
306            let encoded = bp256::r1::Sec1Point::from_affine_coordinates(
307                &bp256::FieldBytes::try_from(*x).map_err(|err| {
308                    ValidationError::InvalidInput(format!(
309                        "Invalid verifying key X length: {err:?}"
310                    ))
311                })?,
312                &bp256::FieldBytes::try_from(*y).map_err(|err| {
313                    ValidationError::InvalidInput(format!(
314                        "Invalid verifying key Y length: {err:?}"
315                    ))
316                })?,
317                false,
318            );
319            VerifyingKey::from_sec1_point(&encoded)
320                .map_err(|e| ValidationError::InvalidInput(format!("{e:?}")))
321        }
322        _ => Err(ValidationError::InvalidInput(
323            "Verifying key must be indicated in compressed-y, or uncompressed form!".into(),
324        )),
325    }?;
326
327    match verifying_key.verify(
328        &[sha256(msg), sha256(encoded_certificate.unwrap_or(&[]))].concat(),
329        &signature,
330    ) {
331        Ok(()) => Ok(ValidationResult::Success),
332        Err(e) => Ok(ValidationResult::Failure {
333            reason: format!("{e:?}"),
334        }),
335    }
336}
337
338fn ecdsa_brainpool_p384_r1(
339    r: &ieee1609dot2::EccP384CurvePoint,
340    s: &[u8],
341    curve_point: &ieee1609dot2::EccP384CurvePoint,
342    msg: &[u8],
343    encoded_certificate: Option<&[u8]>,
344) -> Result<ValidationResult, ValidationError> {
345    let r_unwrapped = match r {
346        ieee1609dot2::EccP384CurvePoint::Fill(()) => {
347            return Ok(ValidationResult::Failure {
348                reason: "R value of signature is not given!".into(),
349            })
350        }
351        ieee1609dot2::EccP384CurvePoint::XOnly(x)
352        | ieee1609dot2::EccP384CurvePoint::CompressedY0(x)
353        | ieee1609dot2::EccP384CurvePoint::CompressedY1(x)
354        | ieee1609dot2::EccP384CurvePoint::UncompressedP384(
355            ieee1609dot2::EccP384CurvePointUncompressedP384 { x, .. },
356        ) => x,
357    };
358
359    let signature = ecdsa::Signature::<bp384::BrainpoolP384r1>::from_scalars(
360        bp384::FieldBytes::try_from(*r_unwrapped)
361            .map_err(|err| ValidationError::InvalidInput(format!("Invalid R length: {err:?}")))?,
362        bp384::FieldBytes::try_from(s)
363            .map_err(|err| ValidationError::InvalidInput(format!("Invalid S length: {err:?}")))?,
364    )
365    .unwrap();
366
367    let verifying_key = match curve_point {
368        ieee1609dot2::EccP384CurvePoint::CompressedY0(x) => {
369            let affine = bp384::r1::AffinePoint::decompress(
370                &bp384::FieldBytes::try_from(*x).map_err(|err| {
371                    ValidationError::InvalidInput(format!(
372                        "Invalid verifying key compressed-y0 length: {err:?}"
373                    ))
374                })?,
375                Choice::from(0),
376            )
377            .unwrap();
378            VerifyingKey::from_affine(affine)
379                .map_err(|e| ValidationError::InvalidInput(format!("{e:?}")))
380        }
381        ieee1609dot2::EccP384CurvePoint::CompressedY1(x) => {
382            let affine = bp384::r1::AffinePoint::decompress(
383                &bp384::FieldBytes::try_from(*x).map_err(|err| {
384                    ValidationError::InvalidInput(format!(
385                        "Invalid verifying key compressed-y1 length: {err:?}"
386                    ))
387                })?,
388                Choice::from(1),
389            )
390            .unwrap();
391            VerifyingKey::from_affine(affine)
392                .map_err(|e| ValidationError::InvalidInput(format!("{e:?}")))
393        }
394        ieee1609dot2::EccP384CurvePoint::UncompressedP384(
395            ieee1609dot2::EccP384CurvePointUncompressedP384 { x, y },
396        ) => {
397            let encoded = bp384::r1::Sec1Point::from_affine_coordinates(
398                &bp384::FieldBytes::try_from(*x).map_err(|err| {
399                    ValidationError::InvalidInput(format!(
400                        "Invalid verifying key X length: {err:?}"
401                    ))
402                })?,
403                &bp384::FieldBytes::try_from(*y).map_err(|err| {
404                    ValidationError::InvalidInput(format!(
405                        "Invalid verifying key Y length: {err:?}"
406                    ))
407                })?,
408                false,
409            );
410            VerifyingKey::from_sec1_point(&encoded)
411                .map_err(|e| ValidationError::InvalidInput(format!("{e:?}")))
412        }
413        _ => Err(ValidationError::InvalidInput(
414            "Verifying key must be indicated in compressed-y, or uncompressed form!".into(),
415        )),
416    }?;
417
418    match verifying_key.verify(
419        &[sha256(msg), sha256(encoded_certificate.unwrap_or(&[]))].concat(),
420        &signature,
421    ) {
422        Ok(()) => Ok(ValidationResult::Success),
423        Err(e) => Ok(ValidationResult::Failure {
424            reason: format!("{e:?}"),
425        }),
426    }
427}
428
429fn ecdsa_nist_p256(
430    r: &ieee1609dot2::EccP256CurvePoint,
431    s: &[u8],
432    curve_point: &ieee1609dot2::EccP256CurvePoint,
433    msg: &[u8],
434    encoded_certificate: Option<&[u8]>,
435) -> Result<ValidationResult, ValidationError> {
436    let r_unwrapped = match r {
437        ieee1609dot2::EccP256CurvePoint::Fill(()) => {
438            return Ok(ValidationResult::Failure {
439                reason: "R value of signature is not given!".into(),
440            })
441        }
442        ieee1609dot2::EccP256CurvePoint::XOnly(x)
443        | ieee1609dot2::EccP256CurvePoint::CompressedY0(x)
444        | ieee1609dot2::EccP256CurvePoint::CompressedY1(x)
445        | ieee1609dot2::EccP256CurvePoint::UncompressedP256(
446            ieee1609dot2::EccP256CurvePointUncompressedP256 { x, .. },
447        ) => x,
448    };
449
450    let signature = ecdsa::Signature::<p256::NistP256>::from_scalars(
451        p256::FieldBytes::try_from(*r_unwrapped)
452            .map_err(|err| ValidationError::InvalidInput(format!("Invalid R length: {err:?}")))?,
453        p256::FieldBytes::try_from(s)
454            .map_err(|err| ValidationError::InvalidInput(format!("Invalid S length: {err:?}")))?,
455    )
456    .unwrap();
457
458    let verifying_key = match curve_point {
459        ieee1609dot2::EccP256CurvePoint::CompressedY0(x) => {
460            let affine = p256::AffinePoint::decompress(
461                &p256::FieldBytes::try_from(*x).map_err(|err| {
462                    ValidationError::InvalidInput(format!(
463                        "Invalid verifying key compressed-y0 length: {err:?}"
464                    ))
465                })?,
466                Choice::from(0),
467            )
468            .unwrap();
469            VerifyingKey::from_affine(affine)
470                .map_err(|e| ValidationError::InvalidInput(format!("{e:?}")))
471        }
472        ieee1609dot2::EccP256CurvePoint::CompressedY1(x) => {
473            let affine = p256::AffinePoint::decompress(
474                &p256::FieldBytes::try_from(*x).map_err(|err| {
475                    ValidationError::InvalidInput(format!(
476                        "Invalid verifying key compressed-y1 length: {err:?}"
477                    ))
478                })?,
479                Choice::from(1),
480            )
481            .unwrap();
482            VerifyingKey::from_affine(affine)
483                .map_err(|e| ValidationError::InvalidInput(format!("{e:?}")))
484        }
485        ieee1609dot2::EccP256CurvePoint::UncompressedP256(
486            ieee1609dot2::EccP256CurvePointUncompressedP256 { x, y },
487        ) => {
488            let encoded = p256::Sec1Point::from_affine_coordinates(
489                &p256::FieldBytes::try_from(*x).map_err(|err| {
490                    ValidationError::InvalidInput(format!(
491                        "Invalid verifying key X length: {err:?}"
492                    ))
493                })?,
494                &p256::FieldBytes::try_from(*y).map_err(|err| {
495                    ValidationError::InvalidInput(format!(
496                        "Invalid verifying key Y length: {err:?}"
497                    ))
498                })?,
499                false,
500            );
501            VerifyingKey::from_sec1_point(&encoded)
502                .map_err(|e| ValidationError::InvalidInput(format!("{e:?}")))
503        }
504        _ => Err(ValidationError::InvalidInput(
505            "Verifying key must be indicated in compressed-y, or uncompressed form!".into(),
506        )),
507    }?;
508
509    match verifying_key.verify(
510        &[sha256(msg), sha256(encoded_certificate.unwrap_or(&[]))].concat(),
511        &signature,
512    ) {
513        Ok(()) => Ok(ValidationResult::Success),
514        Err(e) => Ok(ValidationResult::Failure {
515            reason: format!("{e:?}"),
516        }),
517    }
518}
519
520fn ecdsa_nist_p384(
521    r: &ieee1609dot2::EccP384CurvePoint,
522    s: &[u8],
523    curve_point: &ieee1609dot2::EccP384CurvePoint,
524    msg: &[u8],
525    encoded_certificate: Option<&[u8]>,
526) -> Result<ValidationResult, ValidationError> {
527    let r_unwrapped = match r {
528        ieee1609dot2::EccP384CurvePoint::Fill(()) => {
529            return Ok(ValidationResult::Failure {
530                reason: "R value of signature is not given!".into(),
531            })
532        }
533        ieee1609dot2::EccP384CurvePoint::XOnly(x)
534        | ieee1609dot2::EccP384CurvePoint::CompressedY0(x)
535        | ieee1609dot2::EccP384CurvePoint::CompressedY1(x)
536        | ieee1609dot2::EccP384CurvePoint::UncompressedP384(
537            ieee1609dot2::EccP384CurvePointUncompressedP384 { x, .. },
538        ) => x,
539    };
540
541    let signature = ecdsa::Signature::<p384::NistP384>::from_scalars(
542        p384::FieldBytes::try_from(*r_unwrapped)
543            .map_err(|err| ValidationError::InvalidInput(format!("Invalid R length: {err:?}")))?,
544        p384::FieldBytes::try_from(s)
545            .map_err(|err| ValidationError::InvalidInput(format!("Invalid S length: {err:?}")))?,
546    )
547    .unwrap();
548
549    let verifying_key = match curve_point {
550        ieee1609dot2::EccP384CurvePoint::CompressedY0(x) => {
551            let affine = p384::AffinePoint::decompress(
552                &p384::FieldBytes::try_from(*x).map_err(|err| {
553                    ValidationError::InvalidInput(format!(
554                        "Invalid verifying key compressed-y0 length: {err:?}"
555                    ))
556                })?,
557                Choice::from(0),
558            )
559            .unwrap();
560            VerifyingKey::from_affine(affine)
561                .map_err(|e| ValidationError::InvalidInput(format!("{e:?}")))
562        }
563        ieee1609dot2::EccP384CurvePoint::CompressedY1(x) => {
564            let affine = p384::AffinePoint::decompress(
565                &p384::FieldBytes::try_from(*x).map_err(|err| {
566                    ValidationError::InvalidInput(format!(
567                        "Invalid verifying key compressed-y1 length: {err:?}"
568                    ))
569                })?,
570                Choice::from(1),
571            )
572            .unwrap();
573            VerifyingKey::from_affine(affine)
574                .map_err(|e| ValidationError::InvalidInput(format!("{e:?}")))
575        }
576        ieee1609dot2::EccP384CurvePoint::UncompressedP384(
577            ieee1609dot2::EccP384CurvePointUncompressedP384 { x, y },
578        ) => {
579            let encoded = p384::Sec1Point::from_affine_coordinates(
580                &p384::FieldBytes::try_from(*x).map_err(|err| {
581                    ValidationError::InvalidInput(format!(
582                        "Invalid verifying key X length: {err:?}"
583                    ))
584                })?,
585                &p384::FieldBytes::try_from(*y).map_err(|err| {
586                    ValidationError::InvalidInput(format!(
587                        "Invalid verifying key Y length: {err:?}"
588                    ))
589                })?,
590                false,
591            );
592            VerifyingKey::from_sec1_point(&encoded)
593                .map_err(|e| ValidationError::InvalidInput(format!("{e:?}")))
594        }
595        _ => Err(ValidationError::InvalidInput(
596            "Verifying key must be indicated in compressed-y, or uncompressed form!".into(),
597        )),
598    }?;
599
600    match verifying_key.verify(
601        &[sha384(msg), sha384(encoded_certificate.unwrap_or(&[]))].concat(),
602        &signature,
603    ) {
604        Ok(()) => Ok(ValidationResult::Success),
605        Err(e) => Ok(ValidationResult::Failure {
606            reason: format!("{e:?}"),
607        }),
608    }
609}
610
611fn ecdsa_sm2(
612    r: &[u8],
613    s: &[u8],
614    curve_point: &ieee1609dot2::EccP256CurvePoint,
615    msg: &[u8],
616    encoded_certificate: Option<&[u8]>,
617) -> Result<ValidationResult, ValidationError> {
618    let signature = sm2::dsa::Signature::from_scalars(
619        sm2::FieldBytes::try_from(r)
620            .map_err(|err| ValidationError::InvalidInput(format!("Invalid R length: {err:?}")))?,
621        sm2::FieldBytes::try_from(s)
622            .map_err(|err| ValidationError::InvalidInput(format!("Invalid S length: {err:?}")))?,
623    )
624    .unwrap();
625
626    let verifying_key = match curve_point {
627        ieee1609dot2::EccP256CurvePoint::CompressedY0(x) => {
628            let affine = sm2::AffinePoint::decompress(
629                &sm2::FieldBytes::try_from(*x).map_err(|err| {
630                    ValidationError::InvalidInput(format!(
631                        "Invalid verifying key compressed-y0 length: {err:?}"
632                    ))
633                })?,
634                Choice::from(0),
635            )
636            .unwrap();
637            sm2::dsa::VerifyingKey::from_affine("verifier", affine)
638                .map_err(|e| ValidationError::InvalidInput(format!("{e:?}")))
639        }
640        ieee1609dot2::EccP256CurvePoint::CompressedY1(x) => {
641            let affine = sm2::AffinePoint::decompress(
642                &sm2::FieldBytes::try_from(*x).map_err(|err| {
643                    ValidationError::InvalidInput(format!(
644                        "Invalid verifying key compressed-y1 length: {err:?}"
645                    ))
646                })?,
647                Choice::from(1),
648            )
649            .unwrap();
650            sm2::dsa::VerifyingKey::from_affine("verifier", affine)
651                .map_err(|e| ValidationError::InvalidInput(format!("{e:?}")))
652        }
653        ieee1609dot2::EccP256CurvePoint::UncompressedP256(
654            ieee1609dot2::EccP256CurvePointUncompressedP256 { x, y },
655        ) => {
656            let affine = sm2::AffinePoint::decompress(
657                &sm2::FieldBytes::try_from(*x).map_err(|err| {
658                    ValidationError::InvalidInput(format!(
659                        "Invalid verifying key uncompressed X length: {err:?}"
660                    ))
661                })?,
662                Choice::from(u8::from(!y.last().unwrap().is_even())),
663            )
664            .unwrap();
665            sm2::dsa::VerifyingKey::from_affine("verifier", affine)
666                .map_err(|e| ValidationError::InvalidInput(format!("{e:?}")))
667        }
668        _ => Err(ValidationError::InvalidInput(
669            "Verifying key must be indicated in compressed-y, or uncompressed form!".into(),
670        )),
671    }?;
672
673    match verifying_key.verify(
674        &[sm3(msg), sm3(encoded_certificate.unwrap_or(&[]))].concat(),
675        &signature,
676    ) {
677        Ok(()) => Ok(ValidationResult::Success),
678        Err(e) => Ok(ValidationResult::Failure {
679            reason: format!("{e:?}"),
680        }),
681    }
682}
683
684#[cfg(test)]
685mod tests {
686    use crate::Decode as _;
687
688    use super::*;
689
690    #[test]
691    fn verifies_ecdsa_nist_p256() {
692        // Msg No. 3 rx_r1a.pcap
693        println!(
694            "{:?}",
695            ecdsa_nist_p256(
696                &ieee1609dot2::EccP256CurvePoint::CompressedY0(&[
697                    0x3c, 0xa4, 0x68, 0x09, 0x0a, 0xeb, 0xdd, 0x3e, 0x63, 0xaf, 0x42, 0x1a, 0x91,
698                    0x10, 0x17, 0x76, 0x98, 0x9b, 0x32, 0xef, 0x64, 0xbf, 0x00, 0x5d, 0x4c, 0x10,
699                    0x44, 0xd6, 0x88, 0x79, 0x49, 0x9b,
700                ]),
701                &[
702                    0xb6, 0xd4, 0xbe, 0x84, 0xb3, 0x31, 0x86, 0x96, 0x80, 0x46, 0xff, 0xa3, 0x48,
703                    0xc1, 0xe8, 0x6a, 0x0a, 0x9c, 0xa0, 0x71, 0x2c, 0xa6, 0xd0, 0x4f, 0x93, 0x4e,
704                    0x92, 0xcc, 0x99, 0x45, 0xd2, 0xe8,
705                ],
706                &ieee1609dot2::EccP256CurvePoint::CompressedY0(&[
707                    0x13, 0x43, 0x08, 0xc4, 0x32, 0x4d, 0x5f, 0x47, 0xfc, 0xbe, 0x66, 0x5f, 0xb5,
708                    0x5b, 0x40, 0x98, 0xb3, 0x8b, 0x9c, 0xaa, 0x48, 0x4b, 0xd4, 0x47, 0x4c, 0x6c,
709                    0x52, 0x16, 0x00, 0xa7, 0x50, 0x8c,
710                ]),
711                &[
712                    0x40, 0x03, 0x80, 0x78, 0x20, 0x50, 0x02, 0x80, 0x00, 0x54, 0x01, 0x00, 0x14,
713                    0x00, 0xca, 0x83, 0x1a, 0x3f, 0x3d, 0x39, 0x70, 0xfc, 0x86, 0x68, 0x1f, 0xeb,
714                    0x32, 0x07, 0x05, 0xec, 0x3a, 0xd3, 0x80, 0x04, 0x0b, 0x90, 0x00, 0x00, 0x00,
715                    0x00, 0x07, 0xd1, 0x00, 0x00, 0x02, 0x02, 0x1a, 0x3f, 0x3d, 0x39, 0x86, 0x68,
716                    0x40, 0x5a, 0xb2, 0x03, 0x60, 0xee, 0x26, 0xc1, 0x9a, 0x60, 0xb0, 0x0b, 0x00,
717                    0x00, 0x34, 0x87, 0x8e, 0x48, 0xb9, 0x1f, 0xa0, 0x01, 0x10, 0x82, 0xe8, 0x92,
718                    0x83, 0x33, 0xff, 0x01, 0xff, 0xfa, 0x00, 0x28, 0x33, 0x00, 0x00, 0x4b, 0xff,
719                    0x74, 0xff, 0x2a, 0x2e, 0x68, 0x0c, 0xbb, 0xdf, 0xa4, 0x48, 0x24, 0x7e, 0x23,
720                    0xd3, 0xc8, 0x1f, 0x02, 0x4a, 0xbe, 0xa5, 0xe8, 0xcf, 0x09, 0x69, 0xf8, 0x0d,
721                    0xed, 0xf4, 0x24, 0x4c, 0x90, 0x33, 0x3f, 0x40, 0x01, 0x24, 0x00, 0x02, 0x30,
722                    0x51, 0x5a, 0x70, 0x30, 0x2c
723                ],
724                Some(&[
725                    0x80, 0x03, 0x00, 0x80, 0x5d, 0x5d, 0xcb, 0xee, 0xfb, 0xe7, 0xd2, 0x2d, 0x30,
726                    0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x81, 0xd9, 0x85, 0x86, 0x00, 0x01,
727                    0xe0, 0x01, 0x07, 0x80, 0x01, 0x24, 0x81, 0x04, 0x03, 0x01, 0xff, 0xfc, 0x80,
728                    0x01, 0x25, 0x81, 0x05, 0x04, 0x01, 0xff, 0xff, 0xff, 0x80, 0x01, 0x8c, 0x81,
729                    0x05, 0x04, 0x02, 0xff, 0xff, 0xe0, 0x00, 0x01, 0x8d, 0x80, 0x02, 0x02, 0x7e,
730                    0x81, 0x02, 0x01, 0x01, 0x80, 0x02, 0x02, 0x7f, 0x81, 0x02, 0x01, 0x01, 0x00,
731                    0x02, 0x03, 0xff, 0x80, 0x80, 0x82, 0x13, 0x43, 0x08, 0xc4, 0x32, 0x4d, 0x5f,
732                    0x47, 0xfc, 0xbe, 0x66, 0x5f, 0xb5, 0x5b, 0x40, 0x98, 0xb3, 0x8b, 0x9c, 0xaa,
733                    0x48, 0x4b, 0xd4, 0x47, 0x4c, 0x6c, 0x52, 0x16, 0x00, 0xa7, 0x50, 0x8c, 0x81,
734                    0x80, 0x3d, 0x9a, 0x96, 0x8a, 0xc1, 0x19, 0x6e, 0x46, 0xea, 0x98, 0x22, 0x6c,
735                    0x55, 0x20, 0x81, 0xa7, 0x7c, 0xdf, 0xbe, 0xd5, 0x8c, 0x76, 0x9a, 0xf2, 0x8c,
736                    0x9f, 0xf9, 0x06, 0xe9, 0x26, 0xd9, 0x22, 0x40, 0x5f, 0x18, 0x9a, 0x1c, 0x6a,
737                    0x03, 0x19, 0x89, 0x68, 0x96, 0x0a, 0x93, 0x32, 0x50, 0x06, 0xaf, 0xfb, 0x84,
738                    0x40, 0x4c, 0x93, 0x16, 0x80, 0x69, 0x8f, 0xff, 0x27, 0xc8, 0xf3, 0x12, 0x7e,
739                ])
740            )
741        );
742    }
743
744    #[test]
745    // Cohda demo cert CAM with full cert
746    fn validates_nist_p256_cam() {
747        let data: &'static [u8] = &[
748            0x12, 0x00, 0x05, 0x01, 0x03, 0x81, 0x00, 0x40, 0x03, 0x80, 0x5f, 0x20, 0x50, 0x02,
749            0x80, 0x00, 0x3b, 0x01, 0x00, 0x14, 0x00, 0x06, 0x42, 0x7e, 0x75, 0x45, 0x23, 0x30,
750            0x3e, 0xbe, 0x08, 0x1f, 0xf3, 0x49, 0x66, 0x05, 0xfa, 0x99, 0x4c, 0x80, 0x00, 0x02,
751            0x20, 0x00, 0x00, 0x00, 0x00, 0x07, 0xd1, 0x00, 0x00, 0x02, 0x02, 0x7e, 0x75, 0x45,
752            0x23, 0xbe, 0x08, 0x40, 0x5a, 0xb3, 0x06, 0x4c, 0xce, 0x28, 0x8d, 0x69, 0x86, 0xaa,
753            0x6a, 0xa0, 0x00, 0x34, 0x2e, 0xd0, 0x48, 0x22, 0x0f, 0xa0, 0x05, 0xad, 0xbf, 0xe9,
754            0xea, 0x77, 0x33, 0xff, 0x01, 0xff, 0xfa, 0x00, 0x28, 0x33, 0x00, 0x00, 0x1c, 0x00,
755            0x69, 0x00, 0x4b, 0x31, 0xf6, 0x00, 0x27, 0x80, 0x40, 0x01, 0x24, 0x00, 0x02, 0x79,
756            0x8c, 0x75, 0x19, 0x83, 0x74, 0x81, 0x01, 0x01, 0x80, 0x03, 0x00, 0x80, 0x5d, 0x5d,
757            0xcb, 0xee, 0xfb, 0xe7, 0xd2, 0x2d, 0x30, 0x83, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29,
758            0x84, 0x9d, 0x05, 0x86, 0x00, 0x01, 0xe0, 0x01, 0x07, 0x80, 0x01, 0x24, 0x81, 0x04,
759            0x03, 0x01, 0xff, 0xfc, 0x80, 0x01, 0x25, 0x81, 0x05, 0x04, 0x01, 0xff, 0xff, 0xff,
760            0x80, 0x01, 0x8c, 0x81, 0x05, 0x04, 0x02, 0xff, 0xff, 0xe0, 0x00, 0x01, 0x8d, 0x80,
761            0x02, 0x02, 0x7e, 0x81, 0x02, 0x01, 0x01, 0x80, 0x02, 0x02, 0x7f, 0x81, 0x02, 0x01,
762            0x01, 0x00, 0x02, 0x03, 0xff, 0x80, 0x80, 0x83, 0x7b, 0x2f, 0x6b, 0x07, 0x93, 0xc0,
763            0xd8, 0x89, 0x44, 0x7f, 0xa6, 0xe3, 0xa5, 0x7a, 0x4d, 0xca, 0x9c, 0x3f, 0xe8, 0x3e,
764            0xa5, 0x25, 0xfd, 0x11, 0x61, 0x6e, 0x0d, 0xfd, 0xe9, 0x91, 0x97, 0x39, 0x81, 0x80,
765            0x28, 0x13, 0x79, 0x86, 0x20, 0xa7, 0x29, 0xcc, 0xe6, 0x7d, 0x8d, 0x70, 0x1a, 0x26,
766            0x94, 0x8d, 0x64, 0x35, 0x61, 0x02, 0xd1, 0xd1, 0x99, 0xfb, 0x58, 0xfa, 0x7f, 0xaa,
767            0x70, 0xa9, 0x49, 0xf6, 0x6f, 0x12, 0x84, 0xb5, 0xd3, 0x83, 0xcf, 0x62, 0xa2, 0x7c,
768            0x3a, 0x89, 0x45, 0x73, 0x57, 0x6f, 0x5e, 0x4a, 0xef, 0x50, 0x6c, 0xe6, 0x0d, 0xf1,
769            0xfe, 0x68, 0x70, 0x05, 0x0d, 0x17, 0xa0, 0x12, 0x80, 0x82, 0xdf, 0x57, 0x00, 0xe7,
770            0xbf, 0x19, 0x6e, 0x1d, 0x1d, 0xcc, 0x9c, 0xf7, 0xfe, 0x55, 0x41, 0xd4, 0x53, 0x98,
771            0x6d, 0x95, 0x82, 0x49, 0x3c, 0xad, 0x52, 0x26, 0x55, 0xdf, 0xcc, 0x89, 0x7c, 0x23,
772            0xd2, 0x8e, 0x9e, 0x93, 0x3c, 0x76, 0x9d, 0xf3, 0xf7, 0x18, 0x1b, 0x02, 0x7a, 0x0f,
773            0x80, 0x3c, 0xfb, 0xc2, 0x37, 0x43, 0x3f, 0x8d, 0x5b, 0x7e, 0x83, 0x7d, 0x9e, 0x9d,
774            0x38, 0xd0, 0x62, 0xe6,
775        ];
776
777        let decoded = Packet::decode(data).unwrap();
778        assert_eq!(Ok(ValidationResult::Success), decoded.decoded.validate());
779    }
780
781    #[test]
782    // Cohda demo cert CAM with digest
783    fn validates_nist_p256_cam_digest() {
784        let data: &'static [u8] = &[
785            0x12, 0x00, 0x05, 0x01, 0x03, 0x81, 0x00, 0x40, 0x03, 0x80, 0x54, 0x20, 0x50, 0x02,
786            0x80, 0x00, 0x30, 0x01, 0x00, 0x14, 0x00, 0x06, 0x42, 0x7e, 0x75, 0x45, 0x23, 0x30,
787            0x3e, 0xbe, 0xd0, 0x1f, 0xf3, 0x49, 0x68, 0x05, 0xfa, 0x99, 0x49, 0x80, 0x0c, 0x02,
788            0x20, 0x00, 0x00, 0x00, 0x00, 0x07, 0xd1, 0x00, 0x00, 0x02, 0x02, 0x7e, 0x75, 0x45,
789            0x23, 0xbe, 0xd0, 0x00, 0x5a, 0xb3, 0x06, 0x4d, 0x0e, 0x28, 0x8d, 0x69, 0x26, 0xae,
790            0x6a, 0xe0, 0x00, 0x34, 0x2d, 0x92, 0x48, 0x22, 0x0f, 0xa0, 0x00, 0x98, 0xbf, 0xe9,
791            0xea, 0x6f, 0x33, 0xff, 0x01, 0xff, 0xfa, 0x00, 0x28, 0x33, 0x00, 0x40, 0x01, 0x24,
792            0x00, 0x02, 0x79, 0x8c, 0x75, 0x1b, 0x05, 0xc6, 0x80, 0xe7, 0x3f, 0x07, 0x42, 0x7e,
793            0x75, 0x45, 0x23, 0x80, 0x83, 0x6f, 0xc4, 0xa9, 0x4f, 0xb0, 0xe0, 0x1d, 0xd7, 0x66,
794            0xc3, 0x28, 0x1d, 0xfd, 0x38, 0x06, 0x55, 0xa8, 0x4c, 0xc4, 0x46, 0xc8, 0x7b, 0x33,
795            0x75, 0x6d, 0x56, 0xfc, 0x26, 0x08, 0x6b, 0x55, 0x15, 0xac, 0x23, 0x8b, 0xfe, 0xd1,
796            0x70, 0x21, 0x71, 0x6e, 0x6c, 0x95, 0x7c, 0x6e, 0xf7, 0xb5, 0xa1, 0x5f, 0x6e, 0x89,
797            0x24, 0xfc, 0x21, 0x6e, 0xd4, 0x8f, 0xc2, 0x9a, 0x51, 0x94, 0xba, 0xd0, 0x7f,
798        ];
799
800        let decoded = Packet::decode(data).unwrap();
801
802        assert!(matches!(
803            decoded.decoded.validate(),
804            Err(ValidationError::Unsupported(_))
805        ));
806    }
807
808    #[test]
809    // Some CAM with Brainpool P256r1 certificate
810    fn validates_brainpool_p256r1_cam() {
811        let data: &'static [u8] = &[
812            0x12, 0x00, 0x1a, 0x02, 0x03, 0x81, 0x00, 0x40, 0x03, 0x80, 0x74, 0x20, 0x40, 0x01,
813            0x00, 0x00, 0x40, 0x02, 0x00, 0x33, 0x9f, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x50, 0x56,
814            0xa2, 0x42, 0x5e, 0x16, 0x80, 0xb8, 0x27, 0x1c, 0x0e, 0x3f, 0x53, 0x09, 0x30, 0xe5,
815            0x2f, 0x80, 0x00, 0x00, 0x00, 0x1c, 0x0e, 0x5f, 0x5a, 0x09, 0x30, 0xdc, 0xf1, 0x03,
816            0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xd2, 0x07, 0xd2, 0x02, 0x01, 0x00,
817            0x00, 0x9c, 0x3f, 0xc3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x32, 0x14, 0x42, 0xd0, 0x17,
818            0x04, 0xe5, 0x10, 0xb4, 0x05, 0xc1, 0x3d, 0x1b, 0x34, 0x85, 0xa7, 0x47, 0xaa, 0xef,
819            0x1f, 0xff, 0xff, 0xfe, 0x11, 0xdb, 0xba, 0x1f, 0x40, 0x0f, 0x01, 0xe2, 0x00, 0x40,
820            0x00, 0x04, 0x80, 0x00, 0x60, 0x00, 0x1c, 0x73, 0x87, 0x7f, 0xda, 0x08, 0x3b, 0xc7,
821            0x38, 0x50, 0x01, 0x25, 0x00, 0x02, 0x79, 0x27, 0xd8, 0xb3, 0x39, 0x38, 0x1c, 0x0e,
822            0x3f, 0x53, 0x09, 0x30, 0xe5, 0x2f, 0x10, 0x00, 0x81, 0x01, 0x01, 0x80, 0x03, 0x00,
823            0x80, 0xfb, 0x9f, 0xe6, 0x57, 0x1f, 0x7c, 0xe7, 0xf9, 0x10, 0x83, 0x00, 0x00, 0x00,
824            0x00, 0x00, 0x29, 0x77, 0x14, 0xd9, 0x84, 0x00, 0xa8, 0x01, 0x02, 0x80, 0x01, 0x25,
825            0x81, 0x05, 0x04, 0x01, 0xff, 0xff, 0xff, 0x80, 0x01, 0x8b, 0x81, 0x07, 0x06, 0x01,
826            0xc0, 0x40, 0x01, 0xff, 0xf8, 0x80, 0x81, 0x82, 0x37, 0x9e, 0x96, 0x25, 0xd2, 0xdd,
827            0xb4, 0x4a, 0xe3, 0x00, 0xf1, 0x7c, 0x1c, 0x34, 0xb9, 0xaf, 0xb6, 0x91, 0x24, 0x8a,
828            0x2d, 0xec, 0xf4, 0x72, 0x1f, 0x49, 0x6e, 0x47, 0x0e, 0x98, 0x86, 0x47, 0x80, 0x80,
829            0xd7, 0xa0, 0xad, 0xd8, 0x85, 0xff, 0xae, 0x32, 0x76, 0xdb, 0xed, 0x6b, 0x90, 0x5d,
830            0x8d, 0x72, 0xbe, 0xf3, 0x71, 0x6b, 0xc3, 0xf9, 0x83, 0xc6, 0x65, 0xff, 0xda, 0x8d,
831            0x16, 0xba, 0x47, 0x2d, 0xdf, 0x46, 0xa7, 0x38, 0xa4, 0xd4, 0x6d, 0xdb, 0x24, 0xac,
832            0xad, 0xa6, 0x08, 0x90, 0xd8, 0x5b, 0xf7, 0x5b, 0xf1, 0xc9, 0xe8, 0x06, 0x59, 0x2c,
833            0xfb, 0x71, 0xc6, 0x23, 0xc4, 0xd9, 0x8b, 0x2f, 0x81, 0x80, 0x9e, 0x35, 0xa5, 0x16,
834            0x38, 0xc0, 0xbe, 0x4d, 0x45, 0x7e, 0xcf, 0x50, 0x34, 0x4b, 0xfe, 0x89, 0xef, 0x37,
835            0x10, 0x04, 0x38, 0xec, 0x33, 0x60, 0x77, 0x86, 0x47, 0xb1, 0xe5, 0x9d, 0x9b, 0xc6,
836            0x2c, 0xe3, 0xb8, 0x2a, 0x24, 0xfd, 0x39, 0xee, 0x04, 0xac, 0x90, 0xe2, 0x19, 0x99,
837            0x6a, 0xa1, 0x37, 0x00, 0x57, 0x0c, 0x7c, 0x60, 0xc7, 0x30, 0x72, 0xde, 0x3d, 0xc7,
838            0x9a, 0xb7, 0x32, 0x60,
839        ];
840
841        let decoded = Packet::decode(data).unwrap();
842        assert_eq!(Ok(ValidationResult::Success), decoded.decoded.validate());
843    }
844}