nss/
pkixc.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5use crate::error::*;
6use crate::util::assert_nss_initialized;
7
8use nss_sys::PRErrorCode;
9
10// NSS error codes.
11// https://searchfox.org/mozilla-central/rev/352b525/security/nss/lib/util/secerr.h#29
12const SEC_ERROR_BASE: i32 = -0x2000; // -8192
13const SEC_ERROR_EXPIRED_CERTIFICATE: i32 = SEC_ERROR_BASE + 11;
14const SEC_ERROR_UNKNOWN_ISSUER: i32 = SEC_ERROR_BASE + 13;
15const SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: i32 = SEC_ERROR_BASE + 30;
16
17// SSL error codes.
18// https://searchfox.org/mozilla-central/rev/352b525/security/nss/lib/ssl/sslerr.h#42
19const SSL_ERROR_BASE: i32 = -0x3000; // -12288
20const SSL_ERROR_BAD_CERT_DOMAIN: i32 = SSL_ERROR_BASE + 12;
21
22// PKIX error codes.
23// https://searchfox.org/mozilla-central/rev/352b525/security/nss/lib/mozpkix/include/pkix/pkixnss.h#81
24const PKIX_ERROR_BASE: i32 = -0x4000; // -16384
25const PKIX_ERROR_NOT_YET_VALID_CERTIFICATE: i32 = PKIX_ERROR_BASE + 5;
26const PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE: i32 = PKIX_ERROR_BASE + 6;
27
28const ROOT_HASH_LENGTH: usize = 32;
29
30pub fn verify_code_signing_certificate_chain(
31    certificates: Vec<&[u8]>,
32    seconds_since_epoch: u64,
33    root_sha256_hash: &[u8],
34    hostname: &str,
35) -> Result<()> {
36    assert_nss_initialized();
37
38    let mut cert_lens: Vec<u16> = vec![];
39    for certificate in &certificates {
40        match u16::try_from(certificate.len()) {
41            Ok(v) => cert_lens.push(v),
42            Err(e) => {
43                return Err(ErrorKind::InputError(format!(
44                    "certificate length is more than 65536 bytes: {}",
45                    e
46                ))
47                .into());
48            }
49        }
50    }
51
52    let mut p_certificates: Vec<_> = certificates.iter().map(|c| c.as_ptr()).collect();
53
54    if root_sha256_hash.len() != ROOT_HASH_LENGTH {
55        return Err(ErrorKind::InputError(format!(
56            "root hash contains {} bytes instead of {}",
57            root_sha256_hash.len(),
58            ROOT_HASH_LENGTH
59        ))
60        .into());
61    }
62
63    let mut out: PRErrorCode = 0;
64
65    let result = unsafe {
66        nss_sys::VerifyCodeSigningCertificateChain(
67            p_certificates.as_mut_ptr(), // Ideally the exposed API should not require mutability here.
68            cert_lens.as_ptr(),
69            certificates.len(),
70            seconds_since_epoch,
71            root_sha256_hash.as_ptr(),
72            hostname.as_ptr(),
73            hostname.len(),
74            &mut out,
75        )
76    };
77
78    if !result {
79        let kind = match out {
80            SEC_ERROR_UNKNOWN_ISSUER => ErrorKind::CertificateIssuerError,
81            SEC_ERROR_EXPIRED_CERTIFICATE => ErrorKind::CertificateValidityError,
82            SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE => ErrorKind::CertificateValidityError,
83            PKIX_ERROR_NOT_YET_VALID_CERTIFICATE => ErrorKind::CertificateValidityError,
84            PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE => ErrorKind::CertificateValidityError,
85            SSL_ERROR_BAD_CERT_DOMAIN => ErrorKind::CertificateSubjectError,
86            _ => {
87                let msg = "invalid chain of trust".to_string();
88                if SSL_ERROR_BASE < out && out < SSL_ERROR_BASE + 1000 {
89                    ErrorKind::SSLError(out, msg)
90                } else if PKIX_ERROR_BASE < out && out < PKIX_ERROR_BASE + 1000 {
91                    ErrorKind::PKIXError(out, msg)
92                } else {
93                    ErrorKind::NSSError(out, msg)
94                }
95            }
96        };
97        return Err(kind.into());
98    }
99
100    Ok(())
101}