1use std::str;
6
7use base64::{
8 engine::general_purpose::{STANDARD, URL_SAFE},
9 Engine,
10};
11
12use crate::error::*;
13use crate::signature;
14
15fn decode_root_hash(input: &str) -> Result<Vec<u8>> {
25 let bytes_hex = input.split(':');
26
27 let mut result: Vec<u8> = vec![];
28 for byte_hex in bytes_hex {
29 let byte = match hex::decode(byte_hex) {
30 Ok(v) => v,
31 Err(_) => return Err(ErrorKind::RootHashFormatError(input.to_string()).into()),
32 };
33 result.extend(byte);
34 }
35
36 Ok(result)
37}
38
39fn split_pem(pem_content: &[u8]) -> Result<Vec<Vec<u8>>> {
42 let pem_str = match str::from_utf8(pem_content) {
43 Ok(v) => v,
44 Err(e) => {
45 return Err(ErrorKind::PEMFormatError(e.to_string()).into());
46 }
47 };
48
49 let pem_lines = pem_str.split('\n');
50
51 let mut blocks: Vec<Vec<u8>> = vec![];
52 let mut block: Vec<u8> = vec![];
53 let mut read = false;
54 for line in pem_lines {
55 if line.contains("-----BEGIN CERTIFICATE") {
56 read = true;
57 } else if line.contains("-----END CERTIFICATE") {
58 read = false;
59 let decoded = match STANDARD.decode(&block) {
60 Ok(v) => v,
61 Err(e) => return Err(ErrorKind::PEMFormatError(e.to_string()).into()),
62 };
63 blocks.push(decoded);
64 block.clear();
65 } else if read {
66 block.extend_from_slice(line.as_bytes());
67 }
68 }
69 if read {
70 return Err(ErrorKind::PEMFormatError("Missing end header".into()).into());
71 }
72 if blocks.is_empty() {
73 return Err(ErrorKind::PEMFormatError("Missing PEM data".into()).into());
74 }
75
76 Ok(blocks)
77}
78
79pub fn verify(
87 input: &[u8],
88 signature: &[u8],
89 pem_bytes: &[u8],
90 seconds_since_epoch: u64,
91 root_sha256_hash: &str,
92 hostname: &str,
93) -> Result<()> {
94 let certificates = split_pem(pem_bytes)?;
95
96 let mut certificates_slices: Vec<&[u8]> = vec![];
97 for certificate in &certificates {
98 certificates_slices.push(certificate);
99 }
100
101 let root_hash_bytes = decode_root_hash(root_sha256_hash)?;
102
103 nss::pkixc::verify_code_signing_certificate_chain(
104 certificates_slices,
105 seconds_since_epoch,
106 &root_hash_bytes,
107 hostname,
108 )
109 .map_err(|err| match err.kind() {
110 nss::ErrorKind::CertificateIssuerError => ErrorKind::CertificateIssuerError,
111 nss::ErrorKind::CertificateValidityError => ErrorKind::CertificateValidityError,
112 nss::ErrorKind::CertificateSubjectError => ErrorKind::CertificateSubjectError,
113 _ => ErrorKind::CertificateChainError(err.to_string()),
114 })?;
115
116 let leaf_cert = certificates.first().unwrap(); let public_key_bytes = match nss::cert::extract_ec_public_key(leaf_cert) {
119 Ok(bytes) => bytes,
120 Err(err) => return Err(ErrorKind::CertificateContentError(err.to_string()).into()),
121 };
122
123 let signature_bytes = match URL_SAFE.decode(signature) {
124 Ok(b) => b,
125 Err(err) => return Err(ErrorKind::SignatureContentError(err.to_string()).into()),
126 };
127
128 if signature_bytes.len() != 96 {
130 return Err(ErrorKind::SignatureContentError(format!(
131 "signature contains {} bytes instead of {}",
132 signature_bytes.len(),
133 96
134 ))
135 .into());
136 }
137 if public_key_bytes.len() != 96 + 1 {
138 return Err(ErrorKind::CertificateContentError(format!(
140 "public key contains {} bytes instead of {}",
141 public_key_bytes.len(),
142 97
143 ))
144 .into());
145 }
146
147 let signature_alg = &signature::ECDSA_P384_SHA384;
148 let public_key = signature::UnparsedPublicKey::new(signature_alg, &public_key_bytes);
149 match public_key.verify(input, &signature_bytes) {
152 Ok(_) => Ok(()),
153 Err(err) => Err(ErrorKind::SignatureMismatchError(err.to_string()).into()),
154 }
155}
156
157#[cfg(test)]
158mod test {
159 use super::*;
160
161 const ROOT_HASH: &str = "3C:01:44:6A:BE:90:36:CE:A9:A0:9A:CA:A3:A5:20:AC:62:8F:20:A7:AE:32:CE:86:1C:B2:EF:B7:0F:A0:C7:45";
162 const VALID_CERT_CHAIN: &[u8] = b"\
163-----BEGIN CERTIFICATE-----
164MIIDBjCCAougAwIBAgIIFml6g0ldRGowCgYIKoZIzj0EAwMwgaMxCzAJBgNVBAYT
165AlVTMRwwGgYDVQQKExNNb3ppbGxhIENvcnBvcmF0aW9uMS8wLQYDVQQLEyZNb3pp
166bGxhIEFNTyBQcm9kdWN0aW9uIFNpZ25pbmcgU2VydmljZTFFMEMGA1UEAww8Q29u
167dGVudCBTaWduaW5nIEludGVybWVkaWF0ZS9lbWFpbEFkZHJlc3M9Zm94c2VjQG1v
168emlsbGEuY29tMB4XDTIxMDIwMzE1MDQwNVoXDTIxMDQyNDE1MDQwNVowgakxCzAJ
169BgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFp
170biBWaWV3MRwwGgYDVQQKExNNb3ppbGxhIENvcnBvcmF0aW9uMRcwFQYDVQQLEw5D
171bG91ZCBTZXJ2aWNlczE2MDQGA1UEAxMtcmVtb3RlLXNldHRpbmdzLmNvbnRlbnQt
172c2lnbmF0dXJlLm1vemlsbGEub3JnMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8pKb
173HX4IiD0SCy+NO7gwKqRRZ8IhGd8PTaIHIBgM6RDLRyDeswXgV+2kGUoHyzkbNKZt
174zlrS3AhqeUCtl1g6ECqSmZBbRTjCpn/UCpCnMLL0T0goxtAB8Rmi3CdM0cBUo4GD
175MIGAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAfBgNVHSME
176GDAWgBQlZawrqt0eUz/t6OdN45oKfmzy6DA4BgNVHREEMTAvgi1yZW1vdGUtc2V0
177dGluZ3MuY29udGVudC1zaWduYXR1cmUubW96aWxsYS5vcmcwCgYIKoZIzj0EAwMD
178aQAwZgIxAPh43Bxl4MxPT6Ra1XvboN5O2OvIn2r8rHvZPWR/jJ9vcTwH9X3F0aLJ
1799FiresnsLAIxAOoAcREYB24gFBeWxbiiXaG7TR/yM1/MXw4qxbN965FFUaoB+5Bc
180fS8//SQGTlCqKQ==
181-----END CERTIFICATE-----
182-----BEGIN CERTIFICATE-----
183MIIF2jCCA8KgAwIBAgIEAQAAADANBgkqhkiG9w0BAQsFADCBqTELMAkGA1UEBhMC
184VVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRwwGgYDVQQK
185ExNBZGRvbnMgVGVzdCBTaWduaW5nMSQwIgYDVQQDExt0ZXN0LmFkZG9ucy5zaWdu
186aW5nLnJvb3QuY2ExMTAvBgkqhkiG9w0BCQEWInNlY29wcytzdGFnZXJvb3RhZGRv
187bnNAbW96aWxsYS5jb20wHhcNMjEwMTExMDAwMDAwWhcNMjQxMTE0MjA0ODU5WjCB
188ozELMAkGA1UEBhMCVVMxHDAaBgNVBAoTE01vemlsbGEgQ29ycG9yYXRpb24xLzAt
189BgNVBAsTJk1vemlsbGEgQU1PIFByb2R1Y3Rpb24gU2lnbmluZyBTZXJ2aWNlMUUw
190QwYDVQQDDDxDb250ZW50IFNpZ25pbmcgSW50ZXJtZWRpYXRlL2VtYWlsQWRkcmVz
191cz1mb3hzZWNAbW96aWxsYS5jb20wdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARw1dyE
192xV5aNiHJPa/fVHO6kxJn3oZLVotJ0DzFZA9r1sQf8i0+v78Pg0/c3nTAyZWfkULz
193vOpKYK/GEGBtisxCkDJ+F3NuLPpSIg3fX25pH0LE15fvASBVcr8tKLVHeOmjggG6
194MIIBtjAMBgNVHRMEBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAWBgNVHSUBAf8EDDAK
195BggrBgEFBQcDAzAdBgNVHQ4EFgQUJWWsK6rdHlM/7ejnTeOaCn5s8ugwgdkGA1Ud
196IwSB0TCBzoAUhtg0HE5Y0RNcmV/YQpjtFA8Z8l2hga+kgawwgakxCzAJBgNVBAYT
197AlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEcMBoGA1UE
198ChMTQWRkb25zIFRlc3QgU2lnbmluZzEkMCIGA1UEAxMbdGVzdC5hZGRvbnMuc2ln
199bmluZy5yb290LmNhMTEwLwYJKoZIhvcNAQkBFiJzZWNvcHMrc3RhZ2Vyb290YWRk
200b25zQG1vemlsbGEuY29tggRgJZg7MDMGCWCGSAGG+EIBBAQmFiRodHRwOi8vYWRk
201b25zLmFsbGl6b20ub3JnL2NhL2NybC5wZW0wTgYDVR0eBEcwRaBDMCCCHi5jb250
202ZW50LXNpZ25hdHVyZS5tb3ppbGxhLm9yZzAfgh1jb250ZW50LXNpZ25hdHVyZS5t
203b3ppbGxhLm9yZzANBgkqhkiG9w0BAQsFAAOCAgEAtGTTzcPzpcdf07kIeRs9vPMx
204qiF8ylW5L/IQ2NzT3sFFAvPW1vW1wZC0xAHMsuVyo+BTGrv+4mlD0AUR9acRfiTZ
2059qyZ3sJbyhQwJAXLKU4YpnzuFOf58T/yOnOdwpH2ky/0FuHskMyfXaAz2Az4JXJH
206TCgggqfdZNvsZ5eOnQlKoC5NadMa8oTI5sd4SyR5ANUPAtYok931MvVSz3IMbwTr
207v4PPWXdl9SGXuOknSqdY6/bS1LGvC2KprsT+PBlvVtS6YgZOH0uCgTTLpnrco87O
208ErzC2PJBA1Ftn3Mbaou6xy7O+YX+reJ6soNUV+0JHOuKj0aTXv0c+lXEAh4Y8nea
209UGhW6+MRGYMOP2NuKv8s2+CtNH7asPq3KuTQpM5RerjdouHMIedX7wpNlNk0CYbg
210VMJLxZfAdwcingLWda/H3j7PxMoAm0N+eA24TGDQPC652ZakYk4MQL/45lm0A5f0
211xLGKEe6JMZcTBQyO7ANWcrpVjKMiwot6bY6S2xU17mf/h7J32JXZJ23OPOKpMS8d
212mljj4nkdoYDT35zFuS1z+5q6R5flLca35vRHzC3XA0H/XJvgOKUNLEW/IiJIqLNi
213ab3Ao0RubuX+CAdFML5HaJmkyuJvL3YtwIOwe93RGcGRZSKZsnMS+uY5QN8+qKQz
214LC4GzWQGSCGDyD+JCVw=
215-----END CERTIFICATE-----
216-----BEGIN CERTIFICATE-----
217MIIHbDCCBVSgAwIBAgIEYCWYOzANBgkqhkiG9w0BAQwFADCBqTELMAkGA1UEBhMC
218VVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRwwGgYDVQQK
219ExNBZGRvbnMgVGVzdCBTaWduaW5nMSQwIgYDVQQDExt0ZXN0LmFkZG9ucy5zaWdu
220aW5nLnJvb3QuY2ExMTAvBgkqhkiG9w0BCQEWInNlY29wcytzdGFnZXJvb3RhZGRv
221bnNAbW96aWxsYS5jb20wHhcNMjEwMjExMjA0ODU5WhcNMjQxMTE0MjA0ODU5WjCB
222qTELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBW
223aWV3MRwwGgYDVQQKExNBZGRvbnMgVGVzdCBTaWduaW5nMSQwIgYDVQQDExt0ZXN0
224LmFkZG9ucy5zaWduaW5nLnJvb3QuY2ExMTAvBgkqhkiG9w0BCQEWInNlY29wcytz
225dGFnZXJvb3RhZGRvbnNAbW96aWxsYS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4IC
226DwAwggIKAoICAQDKRVty/FRsO4Ech6EYleyaKgAueaLYfMSsAIyPC/N8n/P8QcH8
227rjoiMJrKHRlqiJmMBSmjUZVzZAP0XJku0orLKWPKq7cATt+xhGY/RJtOzenMMsr5
228eN02V3GzUd1jOShUpERjzXdaO3pnfZqhdqNYqP9ocqQpyno7bZ3FZQ2vei+bF52k
22951uPioTZo+1zduoR/rT01twGtZm3QpcwU4mO74ysyxxgqEy3kpojq8Nt6haDwzrj
230khV9M6DGPLHZD71QaUiz5lOhD9CS8x0uqXhBhwMUBBkHsUDSxbN4ZhjDDWpCmwaD
231OtbJMUJxDGPCr9qj49QESccb367OeXLrfZ2Ntu/US2Bw9EDfhyNsXr9dg9NHj5yf
2324sDUqBHG0W8zaUvJx5T2Ivwtno1YZLyJwQW5pWeWn8bEmpQKD2KS/3y2UjlDg+YM
233NdNASjFe0fh6I5NCFYmFWA73DpDGlUx0BtQQU/eZQJ+oLOTLzp8d3dvenTBVnKF+
234uwEmoNfZwc4TTWJOhLgwxA4uK+Paaqo4Ap2RGS2ZmVkPxmroB3gL5n3k3QEXvULh
2357v8Psk4+MuNWnxudrPkN38MGJo7ju7gDOO8h1jLD4tdfuAqbtQLduLXzT4DJPA4y
236JBTFIRMIpMqP9CovaS8VPtMFLTrYlFh9UnEGpCeLPanJr+VEj7ae5sc8YwIDAQAB
237o4IBmDCCAZQwDAYDVR0TBAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwFgYDVR0lAQH/
238BAwwCgYIKwYBBQUHAwMwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2VuZXJhdGVk
239IENlcnRpZmljYXRlMDMGCWCGSAGG+EIBBAQmFiRodHRwOi8vYWRkb25zLm1vemls
240bGEub3JnL2NhL2NybC5wZW0wHQYDVR0OBBYEFIbYNBxOWNETXJlf2EKY7RQPGfJd
241MIHZBgNVHSMEgdEwgc6AFIbYNBxOWNETXJlf2EKY7RQPGfJdoYGvpIGsMIGpMQsw
242CQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcx
243HDAaBgNVBAoTE0FkZG9ucyBUZXN0IFNpZ25pbmcxJDAiBgNVBAMTG3Rlc3QuYWRk
244b25zLnNpZ25pbmcucm9vdC5jYTExMC8GCSqGSIb3DQEJARYic2Vjb3BzK3N0YWdl
245cm9vdGFkZG9uc0Btb3ppbGxhLmNvbYIEYCWYOzANBgkqhkiG9w0BAQwFAAOCAgEA
246nowyJv8UaIV7NA0B3wkWratq6FgA1s/PzetG/ZKZDIW5YtfUvvyy72HDAwgKbtap
247Eog6zGI4L86K0UGUAC32fBjE5lWYEgsxNM5VWlQjbgTG0dc3dYiufxfDFeMbAPmD
248DzpIgN3jHW2uRqa/MJ+egHhv7kGFL68uVLboqk/qHr+SOCc1LNeSMCuQqvHwwM0+
249AU1GxhzBWDkealTS34FpVxF4sT5sKLODdIS5HXJr2COHHfYkw2SW/Sfpt6fsOwaF
2502iiDaK4LPWHWhhIYa6yaynJ+6O6KPlpvKYCChaTOVdc+ikyeiSO6AakJykr5Gy7d
251PkkK7MDCxuY6psHj7iJQ59YK7ujQB8QYdzuXBuLLo5hc5gBcq3PJs0fLT2YFcQHA
252dj+olGaDn38T0WI8ycWaFhQfKwATeLWfiQepr8JfoNlC2vvSDzGUGfdAfZfsJJZ8
2535xZxahHoTFGS0mDRfXqzKH5uD578GgjOZp0fULmzkcjWsgzdpDhadGjExRZFKlAy
254iKv8cXTONrGY0fyBDKennuX0uAca3V0Qm6v2VRp+7wG/pywWwc5n+04qgxTQPxgO
2556pPB9UUsNbaLMDR5QPYAWrNhqJ7B07XqIYJZSwGP5xB9NqUZLF4z+AOMYgWtDpmg
256IKdcFKAt3fFrpyMhlfIKkLfmm0iDjmfmIXbDGBJw9SE=
257-----END CERTIFICATE-----";
258 const VALID_INPUT: &[u8] =
259 b"Content-Signature:\x00{\"data\":[],\"last_modified\":\"1603992731957\"}";
260 const VALID_SIGNATURE: &[u8] = b"fJJcOpwdnkjEWFeHXfdOJN6GaGLuDTPGzQOxA2jn6ldIleIk6KqMhZcy2GZv2uYiGwl6DERWwpaoUfQFLyCAOcVjck1qlaaEFZGY1BQba9p99xEc9FNQ3YPPfvSSZqsw";
261 const VALID_HOSTNAME: &str = "remote-settings.content-signature.mozilla.org";
262
263 const INVALID_CERTIFICATE: &[u8] = b"\
264 -----BEGIN CERTIFICATE-----
265 invalidCertificategIFiJLFfdxFlYwCgYIKoZIzj0EAwMwgaMxCzAJBgNVBAYT
266 AlVTMRwwGgYDVQQKExNNb3ppbGxhIENvcnBvcmF0aW9uMS8wLQYDVQQLEyZNb3pp
267 bGxhIEFNTyBQcm9kdWN0aW9uIFNpZ25pbmcgU2VydmljZTFFMEMGA1UEAww8Q29u
268 dGVudCBTaWduaW5nIEludGVybWVkaWF0ZS9lbWFpbEFkZHJlc3M9Zm94c2VjQG1v
269 emlsbGEuY29tMB4XDTIwMDYxNjE3MTYxNVoXDTIwMDkwNDE3MTYxNVowgakxCzAJ
270 BgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1Nb3VudGFp
271 biBWaWV3MRwwGgYDVQQKExNNb3ppbGxhIENvcnBvcmF0aW9uMRcwFQYDVQQLEw5D
272 bG91ZCBTZXJ2aWNlczE2MDQGA1UEAxMtcmVtb3RlLXNldHRpbmdzLmNvbnRlbnQt
273 c2lnbmF0dXJlLm1vemlsbGEub3JnMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEDmOX
274 N5IGlUqCvu6xkOKr020Eo3kY2uPdJO0ZihVUoglk1ktQPss184OajFOMKm/BJX4W
275 IsZUzQoRL8NgGfZDwBjT95Q87lhOWEWs5AU/nMXIYwDp7rpUPaUqw0QLMikdo4GD
276 MIGAMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAfBgNVHSME
277 GDAWgBSgHUoXT4zCKzVF8WPx2nBwp8744TA4BgNVHREEMTAvgi1yZW1vdGUtc2V0
278 dGluZ3MuY29udGVudC1zaWduYXR1cmUubW96aWxsYS5vcmcwCgYIKoZIzj0EAwMD
279 aQAwZgIxAJvyynyPqRmRMqf95FPH5xfcoT3jb/2LOkUifGDtjtZ338ScpT2glUK8
280 HszKVANqXQIxAIygMaeTiD9figEusmHMthBdFoIoHk31x4MHukAy+TWZ863X6/V2
281 6/ZrZMpinvalid==
282 -----END CERTIFICATE-----";
283
284 #[test]
285 fn test_decode_root_hash() {
286 nss::ensure_initialized();
287 assert!(decode_root_hash("meh!").is_err());
288 assert!(decode_root_hash("3C:rr:44").is_err());
289
290 let result = decode_root_hash(ROOT_HASH).unwrap();
291 assert_eq!(
292 result,
293 vec![
294 60, 1, 68, 106, 190, 144, 54, 206, 169, 160, 154, 202, 163, 165, 32, 172, 98, 143,
295 32, 167, 174, 50, 206, 134, 28, 178, 239, 183, 15, 160, 199, 69
296 ]
297 );
298 }
299
300 #[test]
301 fn test_split_pem() {
302 assert!(split_pem(b"meh!").is_err());
303
304 assert!(split_pem(
305 b"-----BEGIN CERTIFICATE-----
306invalidCertificate
307-----END CERTIFICATE-----"
308 )
309 .is_err());
310
311 assert!(split_pem(
312 b"-----BEGIN CERTIFICATE-----
313bGxhIEFNTyBQcm9kdWN0aW9uIFNp
314-----BEGIN CERTIFICATE-----"
315 )
316 .is_err());
317
318 let result = split_pem(
319 b"-----BEGIN CERTIFICATE-----
320AQID
321BAUG
322-----END CERTIFICATE-----
323-----BEGIN CERTIFICATE-----
324/f7/
325-----END CERTIFICATE-----",
326 )
327 .unwrap();
328 assert_eq!(result, vec![vec![1, 2, 3, 4, 5, 6], vec![253, 254, 255]]);
329 }
330
331 #[test]
332 fn test_verify_fails_if_invalid() {
333 nss::ensure_initialized();
334 assert!(verify(
335 b"msg",
336 b"sig",
337 b"-----BEGIN CERTIFICATE-----
338fdfeff
339-----END CERTIFICATE-----",
340 42,
341 ROOT_HASH,
342 "remotesettings.firefox.com",
343 )
344 .is_err());
345 }
346
347 #[test]
348 fn test_verify_fails_if_cert_has_expired() {
349 nss::ensure_initialized();
350 assert!(verify(
351 VALID_INPUT,
352 VALID_SIGNATURE,
353 VALID_CERT_CHAIN,
354 1215559719, ROOT_HASH,
356 VALID_HOSTNAME,
357 )
358 .is_err());
359 }
360
361 #[test]
362 fn test_verify_fails_if_bad_certificate_chain() {
363 nss::ensure_initialized();
364 assert!(verify(
365 VALID_INPUT,
366 VALID_SIGNATURE,
367 INVALID_CERTIFICATE,
368 1615559719, ROOT_HASH,
370 VALID_HOSTNAME,
371 )
372 .is_err());
373 }
374
375 #[test]
376 fn test_verify_fails_if_mismatch() {
377 nss::ensure_initialized();
378 assert!(verify(
379 b"msg",
380 VALID_SIGNATURE,
381 VALID_CERT_CHAIN,
382 1615559719, ROOT_HASH,
384 VALID_HOSTNAME,
385 )
386 .is_err());
387 }
388
389 #[test]
390 fn test_verify_fails_if_bad_hostname() {
391 nss::ensure_initialized();
392 assert!(verify(
393 VALID_INPUT,
394 VALID_SIGNATURE,
395 VALID_CERT_CHAIN,
396 1615559719, ROOT_HASH,
398 "some.hostname.org",
399 )
400 .is_err());
401 }
402
403 #[test]
404 fn test_verify_fails_if_bad_root_hash() {
405 nss::ensure_initialized();
406 assert!(verify(
407 VALID_INPUT,
408 VALID_SIGNATURE,
409 VALID_CERT_CHAIN,
410 1615559719, "00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00",
412 VALID_HOSTNAME,
413 )
414 .is_err());
415 }
416
417 #[test]
418 fn test_verify_succeeds_if_valid() {
419 nss::ensure_initialized();
420 verify(
421 VALID_INPUT,
422 VALID_SIGNATURE,
423 VALID_CERT_CHAIN,
424 1615559719, ROOT_HASH,
426 VALID_HOSTNAME,
427 )
428 .unwrap();
429 }
430}