1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
use serde::{de, ser, Deserialize, Serialize};
use std::fmt;

#[derive(Debug, PartialEq, Clone, Copy)]
#[repr(u8)]
pub enum Version {
    V1 = 0x00,
    V2 = 0x01,
    V3 = 0x02,
}

impl Default for Version {
    fn default() -> Self {
        Self::V1
    }
}

impl Version {
    pub fn from_u8(v: u8) -> Option<Self> {
        match v {
            0x00 => Some(Self::V1),
            0x01 => Some(Self::V2),
            0x02 => Some(Self::V3),
            _ => None,
        }
    }
}

impl Serialize for Version {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: ser::Serializer,
    {
        serializer.serialize_u8(*self as u8)
    }
}

impl<'de> Deserialize<'de> for Version {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: de::Deserializer<'de>,
    {
        struct Visitor;

        impl<'de> de::Visitor<'de> for Visitor {
            type Value = Version;

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                write!(formatter, "a valid version number")
            }

            fn visit_u8<E>(self, v: u8) -> Result<Self::Value, E>
            where
                E: de::Error,
            {
                let version = Version::from_u8(v).ok_or_else(|| {
                    E::invalid_value(
                        de::Unexpected::Other("invalid version number"),
                        &"a valid integer representing a supported version number (0, 1 or 2)",
                    )
                })?;

                if version != Version::V3 {
                    return Err(E::invalid_value(
                        de::Unexpected::Other("unsupported certificate version"),
                        &"only V3 certificate are supported",
                    ));
                }

                Ok(version)
            }
        }

        deserializer.deserialize_u8(Visitor)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use picky_asn1::wrapper::{ApplicationTag9, Implicit};
    use picky_asn1_der::Asn1DerError;

    #[derive(Serialize, Deserialize, Debug, PartialEq)]
    struct OptionalVersionTestStruct {
        #[serde(skip_serializing_if = "version_is_default")]
        version: Implicit<ApplicationTag9<Version>>,
        other_non_optional_integer: u8,
    }

    fn version_is_default(version: &Implicit<ApplicationTag9<Version>>) -> bool {
        (version.0).0 == Version::V1
    }

    #[test]
    fn optional_version() {
        let buffer_with_version: [u8; 10] = [0x30, 0x08, 0xA9, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x6E];

        let non_default = OptionalVersionTestStruct {
            version: ApplicationTag9(Version::V3).into(),
            other_non_optional_integer: 0x6E,
        };

        check_serde!(non_default: OptionalVersionTestStruct in buffer_with_version);

        let buffer_without_version: [u8; 5] = [0x30, 0x03, 0x02, 0x01, 0x6E];

        let default = OptionalVersionTestStruct {
            version: ApplicationTag9(Version::default()).into(),
            other_non_optional_integer: 0x6E,
        };

        check_serde!(default: OptionalVersionTestStruct in buffer_without_version);
    }

    #[test]
    fn unsupported_version() {
        let buffer: [u8; 3] = [0x02, 0x01, 0x0F];

        let version: picky_asn1_der::Result<Version> = picky_asn1_der::from_bytes(&buffer);
        match version {
            Err(Asn1DerError::Message(msg)) => assert_eq!(
                msg,
                "invalid value: invalid version number, expected a valid integer \
                 representing a supported version number (0, 1 or 2)"
            ),
            Err(err) => panic!("invalid error: {}", err),
            Ok(_) => panic!("parsing should have failed but did not"),
        }
    }
}