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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
//! Internal utilities for doing things with XML

// #![deny(unsafe_code)]

use std::io::Write;

use std::str::from_utf8;

use xml::writer::{EventWriter, XmlEvent};

/// Extensions for [openssl::x509::X509] for nicer functionality
pub trait X509Utils {
    /// return an X509 object as a string,
    /// either including the ```--- BEGIN LOLS ---```  or not
    fn get_as_pem_string(&self, includeheaders: bool) -> String;
}

impl X509Utils for openssl::x509::X509 {
    /// return an X509 object as a string,
    /// either including the ```--- BEGIN LOLS ---```  or not
    fn get_as_pem_string(&self, includeheaders: bool) -> String {
        let cert_pem = &self.to_pem().unwrap();
        let cert_pem: String = from_utf8(&cert_pem).unwrap().to_string();

        let result = match includeheaders {
            true => cert_pem,
            false => crate::cert::strip_cert_headers(cert_pem),
        };
        log::debug!(
            "############### start get_as_pem_string includeheaders: {} ###############",
            includeheaders
        );
        log::debug!("{}", result);
        log::debug!("############### end get_as_pem_string ###############");
        result
    }
}

/// Used by the XML Event writer to append events to the response
pub fn write_event<W: Write>(event: XmlEvent, writer: &mut EventWriter<W>) -> String {
    match writer.write(event) {
        Ok(val) => format!("{:?}", val),
        Err(err) => format!("{:?}", err),
    }
}

/// add a signature to the statement
pub fn generate_signedinfo<W: Write>(
    attr: &crate::assertion::Assertion,
    digest: &str,
    writer: &mut EventWriter<W>,
) {
    // start ds:SignedInfo
    write_event(XmlEvent::start_element(("ds", "SignedInfo")).into(), writer);
    // start CanonicalizationMethod Tag
    write_event(
        XmlEvent::start_element(("ds", "CanonicalizationMethod"))
            .attr("Algorithm", "http://www.w3.org/2001/10/xml-exc-c14n#")
            .into(),
        writer,
    );
    //end ds:CanonicalizationMethod
    write_event(XmlEvent::end_element().into(), writer);

    // https://www.w3.org/TR/xmldsig-core/#sec-SignatureMethod

    let test: String = attr.signing_algorithm.into();

    write_event(
        XmlEvent::start_element(("ds", "SignatureMethod"))
            .attr("Algorithm", &test)
            .into(),
        writer,
    );
    //end ds:Algorithm
    write_event(XmlEvent::end_element().into(), writer);

    /*
    TODO: this needs to be a reference to the ID
    5.4.2 References
    Signatures MUST contain a single <ds:Reference> containing a same-document reference to the ID
    attribute value of the root element of the assertion or protocol message being signed. For example, if the
    ID attribute value is "foo", then the URI attribute in the <ds:Reference> element MUST be "#foo".
    */
    write_event(
        XmlEvent::start_element(("ds", "Reference"))
            .attr("URI", &format!("#{}", attr.assertion_id))
            .into(),
        writer,
    );

    write_event(XmlEvent::start_element(("ds", "Transforms")).into(), writer);

    write_event(
        XmlEvent::start_element(("ds", "Transform"))
            .attr(
                "Algorithm",
                "http://www.w3.org/2000/09/xmldsig#enveloped-signature",
            )
            .into(),
        writer,
    );
    //end ds:Transform
    write_event(XmlEvent::end_element().into(), writer);

    write_event(
        XmlEvent::start_element(("ds", "Transform"))
            .attr("Algorithm", "http://www.w3.org/2001/10/xml-exc-c14n#")
            .into(),
        writer,
    );
    //end ds:Transform
    write_event(XmlEvent::end_element().into(), writer);

    //end ds:Transforms
    write_event(XmlEvent::end_element().into(), writer);

    let digestmethod: String = attr.digest_algorithm.into();

    // start ds:DigestMethod
    // <https://www.w3.org/TR/xmldsig-core/#sec-DigestMethod>
    write_event(
        XmlEvent::start_element(("ds", "DigestMethod"))
            .attr("Algorithm", &digestmethod)
            .into(),
        writer,
    );
    //end ds:DigestMethod
    write_event(XmlEvent::end_element().into(), writer);

    // <https://www.w3.org/TR/xmldsig-core/#sec-DigestValue>
    // DigestValue is an element that contains the encoded value of the digest. The digest is always encoded using base64 RFC2045.
    write_event(
        XmlEvent::start_element(("ds", "DigestValue")).into(),
        writer,
    );

    write_event(XmlEvent::characters(&digest), writer);
    //end ds:DigestValue
    write_event(XmlEvent::end_element().into(), writer);

    //end ds:Reference
    write_event(XmlEvent::end_element().into(), writer);

    //end ds:SignedInfo
    write_event(XmlEvent::end_element().into(), writer);
}

/// add a signature to the statement
pub fn add_assertion_signature<W: Write>(
    attr: &crate::assertion::Assertion,
    digest: String,
    base64_encoded_signature: String,
    writer: &mut EventWriter<W>,
) {
    write_event(
        XmlEvent::start_element(("ds", "Signature"))
            .attr("xmlns:ds", "http://www.w3.org/2000/09/xmldsig#")
            .into(),
        writer,
    );

    generate_signedinfo(attr, &digest, writer);

    // start ds:SignatureValue
    write_event(
        XmlEvent::start_element(("ds", "SignatureValue")).into(),
        writer,
    );

    write_event(XmlEvent::characters(&base64_encoded_signature), writer);
    // characters
    // end ds:SignatureValue
    write_event(XmlEvent::end_element().into(), writer);

    // start ds:KeyInfo
    write_event(XmlEvent::start_element(("ds", "KeyInfo")).into(), writer);
    // start ds:X509Data
    write_event(XmlEvent::start_element(("ds", "X509Data")).into(), writer);
    // start ds:X509Certificate
    write_event(
        XmlEvent::start_element(("ds", "X509Certificate")).into(),
        writer,
    );

    let mut stripped_cert = attr.signing_cert.clone().unwrap().get_as_pem_string(false);
    // TODO: is this terrible, or is this terrible? It's terrible, find a better way of cleaning this up.
    stripped_cert = stripped_cert
        .replace("\r\n", "")
        .replace("\n", "")
        .replace(" ", "");
    write_event(XmlEvent::characters(&stripped_cert), writer);
    // end ds:X509Certificate
    write_event(XmlEvent::end_element().into(), writer);
    // end ds:X509Data
    write_event(XmlEvent::end_element().into(), writer);
    // end ds:KeyInfo
    write_event(XmlEvent::end_element().into(), writer);

    //end ds:Signature
    write_event(XmlEvent::end_element().into(), writer);
}