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
use crate::{Any, CheckDerConstraints, Error, Length, Result, SerializeResult, Tag, Tagged, ToDer};
use std::convert::TryFrom;

/// ASN.1 `BOOLEAN` type
///
/// BER objects consider any non-zero value as `true`, and `0` as `false`.
///
/// DER objects must use value `0x0` (`false`) or `0xff` (`true`).
#[derive(Debug, PartialEq)]
pub struct Boolean {
    pub value: u8,
}

impl Boolean {
    /// `BOOLEAN` object for value `false`
    pub const FALSE: Boolean = Boolean::new(0);
    /// `BOOLEAN` object for value `true`
    pub const TRUE: Boolean = Boolean::new(0xff);

    /// Create a new `Boolean` from the provided logical value.
    #[inline]
    pub const fn new(value: u8) -> Self {
        Boolean { value }
    }

    /// Return the `bool` value from this object.
    #[inline]
    pub const fn bool(&self) -> bool {
        self.value != 0
    }
}

impl<'a> TryFrom<Any<'a>> for Boolean {
    type Error = Error;

    fn try_from(any: Any<'a>) -> Result<Boolean> {
        any.tag().assert_eq(Self::TAG)?;
        // X.690 section 8.2.1:
        // The encoding of a boolean value shall be primitive. The contents octets shall consist of a single octet
        if any.header.length != Length::Definite(1) {
            return Err(Error::InvalidLength);
        }
        let value = any.data[0];
        Ok(Boolean { value })
    }
}

impl<'a> CheckDerConstraints for Boolean {
    fn check_constraints(any: &Any) -> Result<()> {
        let c = any.data[0];
        // X.690 section 11.1
        if !(c == 0 || c == 0xff) {
            return Err(Error::DerConstraintFailed);
        }
        Ok(())
    }
}

impl<'a> Tagged for Boolean {
    const TAG: Tag = Tag::Boolean;
}

impl ToDer for Boolean {
    fn to_der_len(&self) -> Result<usize> {
        // 3 = 1 (tag) + 1 (length) + 1 (value)
        Ok(3)
    }

    fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
        writer.write(&[Self::TAG.0 as u8, 0x01]).map_err(Into::into)
    }

    fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
        let b = if self.value != 0 { 0xff } else { 0x00 };
        writer.write(&[b]).map_err(Into::into)
    }

    /// Similar to using `to_der`, but uses header without computing length value
    fn write_der_raw(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
        let sz = writer.write(&[Self::TAG.0 as u8, 0x01, self.value])?;
        Ok(sz)
    }
}

impl<'a> TryFrom<Any<'a>> for bool {
    type Error = Error;

    fn try_from(any: Any<'a>) -> Result<bool> {
        any.tag().assert_eq(Self::TAG)?;
        let b = Boolean::try_from(any)?;
        Ok(b.bool())
    }
}

impl<'a> CheckDerConstraints for bool {
    fn check_constraints(any: &Any) -> Result<()> {
        let c = any.data[0];
        // X.690 section 11.1
        if !(c == 0 || c == 0xff) {
            return Err(Error::DerConstraintFailed);
        }
        Ok(())
    }
}

impl<'a> Tagged for bool {
    const TAG: Tag = Tag::Boolean;
}

impl ToDer for bool {
    fn to_der_len(&self) -> Result<usize> {
        // 3 = 1 (tag) + 1 (length) + 1 (value)
        Ok(3)
    }

    fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
        writer.write(&[Self::TAG.0 as u8, 0x01]).map_err(Into::into)
    }

    fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
        let b = if *self { 0xff } else { 0x00 };
        writer.write(&[b]).map_err(Into::into)
    }
}