diff --git a/Cargo.lock b/Cargo.lock index 1e887d9b3b..5e901bb340 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -844,6 +844,7 @@ dependencies = [ "neqo-crypto", "qlog", "regex", + "strum_macros", "test-fixture", "windows", ] @@ -860,6 +861,7 @@ dependencies = [ "semver", "serde", "serde_derive", + "strum_macros", "test-fixture", "toml", ] @@ -1075,6 +1077,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + [[package]] name = "ryu" version = "1.0.12" @@ -1204,6 +1212,19 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "syn" version = "2.0.87" diff --git a/Cargo.toml b/Cargo.toml index bb46298543..c336ab5a8c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ qlog = { version = "0.13", default-features = false } quinn-udp = { version = "0.5.6", default-features = false, features = ["direct-log", "fast-apple-datapath"] } regex = { version = "1.9", default-features = false, features = ["unicode-perl"] } static_assertions = { version = "1.1", default-features = false } +strum_macros = { version = "0.26", default-features = false } url = { version = "2.5.3", default-features = false, features = ["std"] } [workspace.lints.rust] diff --git a/neqo-common/Cargo.toml b/neqo-common/Cargo.toml index ee11980862..694ff5cfb8 100644 --- a/neqo-common/Cargo.toml +++ b/neqo-common/Cargo.toml @@ -22,6 +22,7 @@ env_logger = { version = "0.10", default-features = false } hex = { version = "0.4", default-features = false, features = ["alloc"], optional = true } log = { workspace = true } qlog = { workspace = true } +strum_macros = { workspace = true } [target."cfg(windows)".dependencies] # Checked against https://searchfox.org/mozilla-central/source/Cargo.lock 2024-11-11 diff --git a/neqo-common/src/tos.rs b/neqo-common/src/tos.rs index ed6a5d2724..68091dc25c 100644 --- a/neqo-common/src/tos.rs +++ b/neqo-common/src/tos.rs @@ -7,23 +7,21 @@ use std::fmt::Debug; use enum_map::Enum; +use strum_macros::FromRepr; /// ECN (Explicit Congestion Notification) codepoints mapped to the /// lower 2 bits of the TOS field. /// -#[derive(Copy, Clone, PartialEq, Eq, Enum, Default, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Enum, Default, Debug, FromRepr)] #[repr(u8)] pub enum IpTosEcn { #[default] /// Not-ECT, Not ECN-Capable Transport, RFC3168 NotEct = 0b00, - /// ECT(1), ECN-Capable Transport(1), RFC8311 and RFC9331 Ect1 = 0b01, - /// ECT(0), ECN-Capable Transport(0), RFC3168 Ect0 = 0b10, - /// CE, Congestion Experienced, RFC3168 Ce = 0b11, } @@ -36,13 +34,7 @@ impl From for u8 { impl From for IpTosEcn { fn from(v: u8) -> Self { - match v & 0b0000_0011 { - 0b00 => Self::NotEct, - 0b01 => Self::Ect1, - 0b10 => Self::Ect0, - 0b11 => Self::Ce, - _ => unreachable!(), - } + Self::from_repr(v & 0b0000_0011).expect("all ECN values are covered") } } @@ -64,76 +56,54 @@ impl IpTosEcn { /// Diffserv codepoints, mapped to the upper six bits of the TOS field. /// -#[derive(Copy, Clone, PartialEq, Eq, Enum, Default, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Enum, Default, Debug, FromRepr)] #[repr(u8)] pub enum IpTosDscp { #[default] /// Class Selector 0, RFC2474 Cs0 = 0b0000_0000, - /// Class Selector 1, RFC2474 Cs1 = 0b0010_0000, - /// Class Selector 2, RFC2474 Cs2 = 0b0100_0000, - /// Class Selector 3, RFC2474 Cs3 = 0b0110_0000, - /// Class Selector 4, RFC2474 Cs4 = 0b1000_0000, - /// Class Selector 5, RFC2474 Cs5 = 0b1010_0000, - /// Class Selector 6, RFC2474 Cs6 = 0b1100_0000, - /// Class Selector 7, RFC2474 Cs7 = 0b1110_0000, - /// Assured Forwarding 11, RFC2597 Af11 = 0b0010_1000, - /// Assured Forwarding 12, RFC2597 Af12 = 0b0011_0000, - /// Assured Forwarding 13, RFC2597 Af13 = 0b0011_1000, - /// Assured Forwarding 21, RFC2597 Af21 = 0b0100_1000, - /// Assured Forwarding 22, RFC2597 Af22 = 0b0101_0000, - /// Assured Forwarding 23, RFC2597 Af23 = 0b0101_1000, - /// Assured Forwarding 31, RFC2597 Af31 = 0b0110_1000, - /// Assured Forwarding 32, RFC2597 Af32 = 0b0111_0000, - /// Assured Forwarding 33, RFC2597 Af33 = 0b0111_1000, - /// Assured Forwarding 41, RFC2597 Af41 = 0b1000_1000, - /// Assured Forwarding 42, RFC2597 Af42 = 0b1001_0000, - /// Assured Forwarding 43, RFC2597 Af43 = 0b1001_1000, - /// Expedited Forwarding, RFC3246 Ef = 0b1011_1000, - /// Capacity-Admitted Traffic, RFC5865 VoiceAdmit = 0b1011_0000, - /// Lower-Effort, RFC8622 Le = 0b0000_0100, } @@ -146,32 +116,7 @@ impl From for u8 { impl From for IpTosDscp { fn from(v: u8) -> Self { - match v & 0b1111_1100 { - 0b0000_0000 => Self::Cs0, - 0b0010_0000 => Self::Cs1, - 0b0100_0000 => Self::Cs2, - 0b0110_0000 => Self::Cs3, - 0b1000_0000 => Self::Cs4, - 0b1010_0000 => Self::Cs5, - 0b1100_0000 => Self::Cs6, - 0b1110_0000 => Self::Cs7, - 0b0010_1000 => Self::Af11, - 0b0011_0000 => Self::Af12, - 0b0011_1000 => Self::Af13, - 0b0100_1000 => Self::Af21, - 0b0101_0000 => Self::Af22, - 0b0101_1000 => Self::Af23, - 0b0110_1000 => Self::Af31, - 0b0111_0000 => Self::Af32, - 0b0111_1000 => Self::Af33, - 0b1000_1000 => Self::Af41, - 0b1001_0000 => Self::Af42, - 0b1001_1000 => Self::Af43, - 0b1011_1000 => Self::Ef, - 0b1011_0000 => Self::VoiceAdmit, - 0b0000_0100 => Self::Le, - _ => unreachable!(), - } + Self::from_repr(v & 0b1111_1100).expect("all DCSP values are covered") } } diff --git a/neqo-crypto/Cargo.toml b/neqo-crypto/Cargo.toml index 86f6ee6e87..d22d746b69 100644 --- a/neqo-crypto/Cargo.toml +++ b/neqo-crypto/Cargo.toml @@ -20,6 +20,7 @@ workspace = true enum-map = { workspace = true } log = { workspace = true } neqo-common = { path = "../neqo-common" } +strum_macros = { workspace = true} [build-dependencies] # Checked against https://searchfox.org/mozilla-central/source/Cargo.lock 2024-11-11 diff --git a/neqo-crypto/src/auth.rs b/neqo-crypto/src/auth.rs index 2932cdf2eb..bacc40c30e 100644 --- a/neqo-crypto/src/auth.rs +++ b/neqo-crypto/src/auth.rs @@ -4,72 +4,42 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use strum_macros::FromRepr; + use crate::err::{mozpkix, sec, ssl, PRErrorCode}; /// The outcome of authentication. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, FromRepr)] +#[repr(i32)] pub enum AuthenticationStatus { Ok, - CaInvalid, - CaNotV3, - CertAlgorithmDisabled, - CertExpired, - CertInvalidTime, - CertIsCa, - CertKeyUsage, - CertMitm, - CertNotYetValid, - CertRevoked, - CertSelfSigned, - CertSubjectInvalid, - CertUntrusted, - CertWeakKey, - IssuerEmptyName, - IssuerExpired, - IssuerNotYetValid, - IssuerUnknown, - IssuerUntrusted, - PolicyRejection, - Unknown, + CaInvalid = sec::SEC_ERROR_CA_CERT_INVALID, + CaNotV3 = mozpkix::MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA, + CertAlgorithmDisabled = sec::SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, + CertExpired = sec::SEC_ERROR_EXPIRED_CERTIFICATE, + CertInvalidTime = sec::SEC_ERROR_INVALID_TIME, + CertIsCa = mozpkix::MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY, + CertKeyUsage = sec::SEC_ERROR_INADEQUATE_KEY_USAGE, + CertMitm = mozpkix::MOZILLA_PKIX_ERROR_MITM_DETECTED, + CertNotYetValid = mozpkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE, + CertRevoked = sec::SEC_ERROR_REVOKED_CERTIFICATE, + CertSelfSigned = mozpkix::MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT, + CertSubjectInvalid = ssl::SSL_ERROR_BAD_CERT_DOMAIN, + CertUntrusted = sec::SEC_ERROR_UNTRUSTED_CERT, + CertWeakKey = mozpkix::MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE, + IssuerEmptyName = mozpkix::MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME, + IssuerExpired = sec::SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE, + IssuerNotYetValid = mozpkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE, + IssuerUnknown = sec::SEC_ERROR_UNKNOWN_ISSUER, + IssuerUntrusted = sec::SEC_ERROR_UNTRUSTED_ISSUER, + PolicyRejection = mozpkix::MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED, + Unknown = sec::SEC_ERROR_LIBRARY_FAILURE, } impl From for PRErrorCode { #[must_use] fn from(v: AuthenticationStatus) -> Self { - match v { - AuthenticationStatus::Ok => 0, - AuthenticationStatus::CaInvalid => sec::SEC_ERROR_CA_CERT_INVALID, - AuthenticationStatus::CaNotV3 => mozpkix::MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA, - AuthenticationStatus::CertAlgorithmDisabled => { - sec::SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED - } - AuthenticationStatus::CertExpired => sec::SEC_ERROR_EXPIRED_CERTIFICATE, - AuthenticationStatus::CertInvalidTime => sec::SEC_ERROR_INVALID_TIME, - AuthenticationStatus::CertIsCa => { - mozpkix::MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY - } - AuthenticationStatus::CertKeyUsage => sec::SEC_ERROR_INADEQUATE_KEY_USAGE, - AuthenticationStatus::CertMitm => mozpkix::MOZILLA_PKIX_ERROR_MITM_DETECTED, - AuthenticationStatus::CertNotYetValid => { - mozpkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE - } - AuthenticationStatus::CertRevoked => sec::SEC_ERROR_REVOKED_CERTIFICATE, - AuthenticationStatus::CertSelfSigned => mozpkix::MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT, - AuthenticationStatus::CertSubjectInvalid => ssl::SSL_ERROR_BAD_CERT_DOMAIN, - AuthenticationStatus::CertUntrusted => sec::SEC_ERROR_UNTRUSTED_CERT, - AuthenticationStatus::CertWeakKey => mozpkix::MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE, - AuthenticationStatus::IssuerEmptyName => mozpkix::MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME, - AuthenticationStatus::IssuerExpired => sec::SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE, - AuthenticationStatus::IssuerNotYetValid => { - mozpkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE - } - AuthenticationStatus::IssuerUnknown => sec::SEC_ERROR_UNKNOWN_ISSUER, - AuthenticationStatus::IssuerUntrusted => sec::SEC_ERROR_UNTRUSTED_ISSUER, - AuthenticationStatus::PolicyRejection => { - mozpkix::MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED - } - AuthenticationStatus::Unknown => sec::SEC_ERROR_LIBRARY_FAILURE, - } + v as Self } } @@ -78,31 +48,6 @@ impl From for PRErrorCode { impl From for AuthenticationStatus { #[must_use] fn from(v: PRErrorCode) -> Self { - match v { - 0 => Self::Ok, - sec::SEC_ERROR_CA_CERT_INVALID => Self::CaInvalid, - mozpkix::MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA => Self::CaNotV3, - sec::SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED => Self::CertAlgorithmDisabled, - sec::SEC_ERROR_EXPIRED_CERTIFICATE => Self::CertExpired, - sec::SEC_ERROR_INVALID_TIME => Self::CertInvalidTime, - mozpkix::MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY => Self::CertIsCa, - sec::SEC_ERROR_INADEQUATE_KEY_USAGE => Self::CertKeyUsage, - mozpkix::MOZILLA_PKIX_ERROR_MITM_DETECTED => Self::CertMitm, - mozpkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE => Self::CertNotYetValid, - sec::SEC_ERROR_REVOKED_CERTIFICATE => Self::CertRevoked, - mozpkix::MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT => Self::CertSelfSigned, - ssl::SSL_ERROR_BAD_CERT_DOMAIN => Self::CertSubjectInvalid, - sec::SEC_ERROR_UNTRUSTED_CERT => Self::CertUntrusted, - mozpkix::MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE => Self::CertWeakKey, - mozpkix::MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME => Self::IssuerEmptyName, - sec::SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE => Self::IssuerExpired, - mozpkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE => Self::IssuerNotYetValid, - sec::SEC_ERROR_UNKNOWN_ISSUER => Self::IssuerUnknown, - sec::SEC_ERROR_UNTRUSTED_ISSUER => Self::IssuerUntrusted, - mozpkix::MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED => { - Self::PolicyRejection - } - _ => Self::Unknown, - } + Self::from_repr(v).unwrap_or(Self::Unknown) } } diff --git a/neqo-crypto/src/constants.rs b/neqo-crypto/src/constants.rs index 5ce615faee..268c0e336b 100644 --- a/neqo-crypto/src/constants.rs +++ b/neqo-crypto/src/constants.rs @@ -5,6 +5,7 @@ // except according to those terms. use enum_map::Enum; +use strum_macros::FromRepr; use crate::{ssl, Error}; @@ -13,7 +14,8 @@ use crate::{ssl, Error}; pub type Alert = u8; -#[derive(Default, Debug, Enum, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Default, Debug, Enum, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, FromRepr)] +#[repr(u16)] pub enum Epoch { // TLS doesn't really have an "initial" concept that maps to QUIC so directly, // but this should be clear enough. @@ -29,24 +31,13 @@ impl TryFrom for Epoch { type Error = Error; fn try_from(value: u16) -> Result { - match value { - 0 => Ok(Self::Initial), - 1 => Ok(Self::ZeroRtt), - 2 => Ok(Self::Handshake), - 3 => Ok(Self::ApplicationData), - _ => Err(Error::InvalidEpoch), - } + Self::from_repr(value).ok_or(Error::InvalidEpoch) } } impl From for usize { fn from(e: Epoch) -> Self { - match e { - Epoch::Initial => 0, - Epoch::ZeroRtt => 1, - Epoch::Handshake => 2, - Epoch::ApplicationData => 3, - } + e as Self } } diff --git a/neqo-crypto/src/secrets.rs b/neqo-crypto/src/secrets.rs index a3c11dceb0..55985e8264 100644 --- a/neqo-crypto/src/secrets.rs +++ b/neqo-crypto/src/secrets.rs @@ -10,6 +10,7 @@ use std::{mem, os::raw::c_void, pin::Pin}; use enum_map::EnumMap; use neqo_common::qdebug; +use strum_macros::FromRepr; use crate::{ agentio::as_c_void, @@ -25,20 +26,17 @@ experimental_api!(SSL_SecretCallback( arg: *mut c_void, )); -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, FromRepr)] +#[repr(u32)] pub enum SecretDirection { - Read, - Write, + Read = SSLSecretDirection::ssl_secret_read, + Write = SSLSecretDirection::ssl_secret_write, } impl From for SecretDirection { #[must_use] fn from(dir: SSLSecretDirection::Type) -> Self { - match dir { - SSLSecretDirection::ssl_secret_read => Self::Read, - SSLSecretDirection::ssl_secret_write => Self::Write, - _ => unreachable!(), - } + Self::from_repr(dir).expect("Invalid secret direction") } } diff --git a/neqo-crypto/src/ssl.rs b/neqo-crypto/src/ssl.rs index e654c991e4..8e855c6036 100644 --- a/neqo-crypto/src/ssl.rs +++ b/neqo-crypto/src/ssl.rs @@ -28,43 +28,27 @@ pub const SECSuccess: SECStatus = _SECStatus_SECSuccess; pub const SECFailure: SECStatus = _SECStatus_SECFailure; #[derive(Debug, Copy, Clone)] +#[repr(u32)] pub enum Opt { - Locking, - Tickets, - OcspStapling, - Alpn, - ExtendedMasterSecret, - SignedCertificateTimestamps, - EarlyData, - RecordSizeLimit, - Tls13CompatMode, - HelloDowngradeCheck, - SuppressEndOfEarlyData, - Grease, - EnableChExtensionPermutation, + Locking = SSLOption::SSL_NO_LOCKS, + Tickets = SSLOption::SSL_ENABLE_SESSION_TICKETS, + OcspStapling = SSLOption::SSL_ENABLE_OCSP_STAPLING, + Alpn = SSLOption::SSL_ENABLE_ALPN, + ExtendedMasterSecret = SSLOption::SSL_ENABLE_EXTENDED_MASTER_SECRET, + SignedCertificateTimestamps = SSLOption::SSL_ENABLE_SIGNED_CERT_TIMESTAMPS, + EarlyData = SSLOption::SSL_ENABLE_0RTT_DATA, + RecordSizeLimit = SSLOption::SSL_RECORD_SIZE_LIMIT, + Tls13CompatMode = SSLOption::SSL_ENABLE_TLS13_COMPAT_MODE, + HelloDowngradeCheck = SSLOption::SSL_ENABLE_HELLO_DOWNGRADE_CHECK, + SuppressEndOfEarlyData = SSLOption::SSL_SUPPRESS_END_OF_EARLY_DATA, + Grease = SSLOption::SSL_ENABLE_GREASE, + EnableChExtensionPermutation = SSLOption::SSL_ENABLE_CH_EXTENSION_PERMUTATION, } impl Opt { - // Cast is safe here because SSLOptions are within the i32 range - #[allow(clippy::cast_possible_wrap)] #[must_use] pub const fn as_int(self) -> PRInt32 { - let i = match self { - Self::Locking => SSLOption::SSL_NO_LOCKS, - Self::Tickets => SSLOption::SSL_ENABLE_SESSION_TICKETS, - Self::OcspStapling => SSLOption::SSL_ENABLE_OCSP_STAPLING, - Self::Alpn => SSLOption::SSL_ENABLE_ALPN, - Self::ExtendedMasterSecret => SSLOption::SSL_ENABLE_EXTENDED_MASTER_SECRET, - Self::SignedCertificateTimestamps => SSLOption::SSL_ENABLE_SIGNED_CERT_TIMESTAMPS, - Self::EarlyData => SSLOption::SSL_ENABLE_0RTT_DATA, - Self::RecordSizeLimit => SSLOption::SSL_RECORD_SIZE_LIMIT, - Self::Tls13CompatMode => SSLOption::SSL_ENABLE_TLS13_COMPAT_MODE, - Self::HelloDowngradeCheck => SSLOption::SSL_ENABLE_HELLO_DOWNGRADE_CHECK, - Self::SuppressEndOfEarlyData => SSLOption::SSL_SUPPRESS_END_OF_EARLY_DATA, - Self::Grease => SSLOption::SSL_ENABLE_GREASE, - Self::EnableChExtensionPermutation => SSLOption::SSL_ENABLE_CH_EXTENSION_PERMUTATION, - }; - i as PRInt32 + self as PRInt32 } // Some options are backwards, like SSL_NO_LOCKS, so use this to manage that.