diff options
Diffstat (limited to 'rust/vendor/ipsec-parser')
-rw-r--r-- | rust/vendor/ipsec-parser/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | rust/vendor/ipsec-parser/.travis.yml | 33 | ||||
-rw-r--r-- | rust/vendor/ipsec-parser/Cargo.toml | 32 | ||||
-rw-r--r-- | rust/vendor/ipsec-parser/LICENSE-APACHE | 201 | ||||
-rw-r--r-- | rust/vendor/ipsec-parser/LICENSE-MIT | 25 | ||||
-rw-r--r-- | rust/vendor/ipsec-parser/assets/ike-sa-init-req.bin | bin | 0 -> 256 bytes | |||
-rw-r--r-- | rust/vendor/ipsec-parser/assets/ike-sa-init-resp.bin | bin | 0 -> 281 bytes | |||
-rw-r--r-- | rust/vendor/ipsec-parser/src/error.rs | 19 | ||||
-rw-r--r-- | rust/vendor/ipsec-parser/src/esp.rs | 61 | ||||
-rw-r--r-- | rust/vendor/ipsec-parser/src/ikev2.rs | 622 | ||||
-rw-r--r-- | rust/vendor/ipsec-parser/src/ikev2_debug.rs | 68 | ||||
-rw-r--r-- | rust/vendor/ipsec-parser/src/ikev2_notify.rs | 90 | ||||
-rw-r--r-- | rust/vendor/ipsec-parser/src/ikev2_parser.rs | 600 | ||||
-rw-r--r-- | rust/vendor/ipsec-parser/src/ikev2_transforms.rs | 271 | ||||
-rw-r--r-- | rust/vendor/ipsec-parser/src/lib.rs | 80 |
15 files changed, 2103 insertions, 0 deletions
diff --git a/rust/vendor/ipsec-parser/.cargo-checksum.json b/rust/vendor/ipsec-parser/.cargo-checksum.json new file mode 100644 index 0000000..861d7e8 --- /dev/null +++ b/rust/vendor/ipsec-parser/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{".travis.yml":"abbc9c4fe3fd7457191640c6e5ff798affdfb8652c1e2f2f0e86d041591b4cc0","Cargo.toml":"ec1a53966170b18a06ccaa184381bb7038f09efc5043ed0df931f619e30f9dad","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"a5c61b93b6ee1d104af9920cf020ff3c7efe818e31fe562c72261847a728f513","assets/ike-sa-init-req.bin":"6e58dcd7b73fc4e0196dd940c8b31c8ccfeb4fe52645f942df1254ffd91e8636","assets/ike-sa-init-resp.bin":"ae8e26165ba9187931c6933b6bc8b42df366b011d8bed1f8eb3ef6d8aa7c8cde","src/error.rs":"f94c6920931d0bd3f25c36e9bd0f54710c5d4f18b4e395240f29cfc5e8d99cd6","src/esp.rs":"bd43e7b3f0ca4482849b04490eade4d8abf4ab580fca160b63cb85af2c7be072","src/ikev2.rs":"b83a2af7f2f92f005ec03a23c5cadc3c1598c0d5893052b2338d6e0ac15f8278","src/ikev2_debug.rs":"b0df475275fa0b475fd220eef151b4f580c3585ffa9427c5ae58715019fbac63","src/ikev2_notify.rs":"2d00cabb66e09a9ec0a098f0466be854a0d125324314928757f47271d9a812f4","src/ikev2_parser.rs":"3715c4ab909d1bd028cb63e2620a3022c5a40c70602a89e80150f381ef292823","src/ikev2_transforms.rs":"63650f99848ed9363b743ab0279c22e48c431bfeb97959645f49836965300ca5","src/lib.rs":"2c80fd4752d4e4bb8337c679f08f5c43babd168b3c37d324e79f1b2afb577438"},"package":"2cf8413e5de78bcbc51880ff71f4b64105719abe6efb8b4b877d3c7dc494ddd1"}
\ No newline at end of file diff --git a/rust/vendor/ipsec-parser/.travis.yml b/rust/vendor/ipsec-parser/.travis.yml new file mode 100644 index 0000000..2c411ac --- /dev/null +++ b/rust/vendor/ipsec-parser/.travis.yml @@ -0,0 +1,33 @@ +language: rust +sudo: false +matrix: + include: + - rust: stable + env: + - NAME="stable" + - FEATURES='' + - rust: stable + env: + - NAME="stable,fmt" + - FEATURES='' + - RUSTFMT=yes + - rust: stable + env: + - NAME="stable,clippy" + - FEATURES='' + - CLIPPY=yes + - rust: nightly + env: + - NAME="nightly" + - FEATURES='' +before_script: + - rustc --version + - ([ "$CLIPPY" != yes ] || rustup component add clippy) + - ([ "$RUSTFMT" != yes ] || rustup component add rustfmt) +script: + - ([ "$RUSTFMT" != yes ] || cargo fmt --all -- --check) + - ([ "$CLIPPY" != yes ] || cargo clippy -- -D clippy::all) + - | + cargo build --verbose --features "$FEATURES" && + cargo test --verbose --features "$FEATURES" && + ([ "$BENCH" != 1 ] || cargo bench --verbose --features "$FEATURES") diff --git a/rust/vendor/ipsec-parser/Cargo.toml b/rust/vendor/ipsec-parser/Cargo.toml new file mode 100644 index 0000000..a75757d --- /dev/null +++ b/rust/vendor/ipsec-parser/Cargo.toml @@ -0,0 +1,32 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "ipsec-parser" +version = "0.7.0" +authors = ["Pierre Chifflier <chifflier@wzdftpd.net>"] +include = ["LICENSE-*", ".gitignore", ".travis.yml", "Cargo.toml", "src/*.rs", "assets/*"] +description = "Parser for the IKEv2 protocol" +homepage = "https://github.com/rusticata/ipsec-parser" +documentation = "https://docs.rs/ipsec-parser" +readme = "README.md" +keywords = ["IPsec", "IKEv2", "protocol", "parser", "nom"] +categories = ["parser-implementations"] +license = "MIT/Apache-2.0" +repository = "https://github.com/rusticata/ipsec-parser.git" +[dependencies.nom] +version = "7.0" + +[dependencies.rusticata-macros] +version = "4.0" +[badges.travis-ci] +repository = "rusticata/ipsec-parser" diff --git a/rust/vendor/ipsec-parser/LICENSE-APACHE b/rust/vendor/ipsec-parser/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/rust/vendor/ipsec-parser/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/rust/vendor/ipsec-parser/LICENSE-MIT b/rust/vendor/ipsec-parser/LICENSE-MIT new file mode 100644 index 0000000..290e7b9 --- /dev/null +++ b/rust/vendor/ipsec-parser/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2017 Pierre Chifflier + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/rust/vendor/ipsec-parser/assets/ike-sa-init-req.bin b/rust/vendor/ipsec-parser/assets/ike-sa-init-req.bin Binary files differnew file mode 100644 index 0000000..7d77876 --- /dev/null +++ b/rust/vendor/ipsec-parser/assets/ike-sa-init-req.bin diff --git a/rust/vendor/ipsec-parser/assets/ike-sa-init-resp.bin b/rust/vendor/ipsec-parser/assets/ike-sa-init-resp.bin Binary files differnew file mode 100644 index 0000000..65688a9 --- /dev/null +++ b/rust/vendor/ipsec-parser/assets/ike-sa-init-resp.bin diff --git a/rust/vendor/ipsec-parser/src/error.rs b/rust/vendor/ipsec-parser/src/error.rs new file mode 100644 index 0000000..4d753fa --- /dev/null +++ b/rust/vendor/ipsec-parser/src/error.rs @@ -0,0 +1,19 @@ +use nom::error::{ErrorKind, ParseError}; + +#[derive(Debug)] +pub enum IPsecError { + PayloadTooSmall, + ExtraBytesInPayload, + PayloadParseError, + + NomError(ErrorKind), +} + +impl<I> ParseError<I> for IPsecError { + fn from_error_kind(_input: I, kind: ErrorKind) -> Self { + IPsecError::NomError(kind) + } + fn append(_input: I, _kind: ErrorKind, other: Self) -> Self { + other + } +} diff --git a/rust/vendor/ipsec-parser/src/esp.rs b/rust/vendor/ipsec-parser/src/esp.rs new file mode 100644 index 0000000..fdfb03e --- /dev/null +++ b/rust/vendor/ipsec-parser/src/esp.rs @@ -0,0 +1,61 @@ +use crate::ikev2::IkeV2Header; +use crate::ikev2_parser::parse_ikev2_header; +use nom::bytes::streaming::take; +use nom::combinator::rest; +use nom::number::streaming::be_u32; +use nom::IResult; + +/// Encapsulating Security Payload Packet Format +/// +/// Defined in [RFC2406](https://tools.ietf.org/html/rfc2406) section 2 +#[derive(Debug)] +pub struct ESPHeader<'a> { + pub spi_index: &'a [u8], + pub seq: u32, + pub data: &'a [u8], +} + +/// UDP-encapsulated Packet Formats +/// +/// Defined in [RFC3948](https://tools.ietf.org/html/rfc3948) section 2 +#[derive(Debug)] +pub enum ESPData<'a> { + ESP(ESPHeader<'a>), + IKE(IkeV2Header), +} + +/// Parse an encapsulated ESP packet +/// +/// The type of encapsulated data depends on the first field (`spi_index`): 0 is a forbidden SPI +/// index, and indicates that the header is an IKE header. +/// Any other value indicates an ESP header. +/// +/// *Note: input is entirely consumed* +pub fn parse_esp_encapsulated(i: &[u8]) -> IResult<&[u8], ESPData> { + if be_u32(i)?.1 == 0 { + parse_ikev2_header(i).map(|x| (x.0, ESPData::IKE(x.1))) + } else { + parse_esp_header(i).map(|x| (x.0, ESPData::ESP(x.1))) + } +} + +/// Parse an ESP packet +/// +/// The ESP header contains: +/// +/// - the SPI index +/// - the sequence number +/// - the payload data (which can be encrypted) +/// +/// *Note: input is entirely consumed* +pub fn parse_esp_header(i: &[u8]) -> IResult<&[u8], ESPHeader> { + let (i, spi_index) = take(4usize)(i)?; + let (i, seq) = be_u32(i)?; + let (i, data) = rest(i)?; + let hdr = ESPHeader { + spi_index, + seq, + data, + }; + Ok((i, hdr)) +} diff --git a/rust/vendor/ipsec-parser/src/ikev2.rs b/rust/vendor/ipsec-parser/src/ikev2.rs new file mode 100644 index 0000000..400f4a1 --- /dev/null +++ b/rust/vendor/ipsec-parser/src/ikev2.rs @@ -0,0 +1,622 @@ +use crate::ikev2_notify::NotifyType; +use crate::ikev2_transforms::*; +use rusticata_macros::newtype_enum; +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; + +/// Payload exchange type: SA, Auth, CreateChildSA, etc. +#[derive(Copy, Clone, PartialEq, Eq)] +pub struct IkeExchangeType(pub u8); + +newtype_enum! { +impl debug IkeExchangeType { + IKE_SA_INIT = 34, + IKE_AUTH = 35, + CREATE_CHILD_SA = 36, + INFORMATIONAL = 37, +} +} + +/// Protocol type: IKE, AH or ESP +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.3.1 +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct ProtocolID(pub u8); + +newtype_enum! { +impl debug ProtocolID { + IKE = 1, + AH = 2, + ESP = 3, +} +} + +pub const IKEV2_FLAG_INITIATOR: u8 = 0b1000; +pub const IKEV2_FLAG_VERSION: u8 = 0b1_0000; +pub const IKEV2_FLAG_RESPONSE: u8 = 0b10_0000; + +/// The IKE Header +/// +/// IKE messages use UDP ports 500 and/or 4500, with one IKE message per +/// UDP datagram. Information from the beginning of the packet through +/// the UDP header is largely ignored except that the IP addresses and +/// UDP ports from the headers are reversed and used for return packets. +/// When sent on UDP port 500, IKE messages begin immediately following +/// the UDP header. When sent on UDP port 4500, IKE messages have +/// prepended four octets of zeros. These four octets of zeros are not +/// part of the IKE message and are not included in any of the length +/// fields or checksums defined by IKE. Each IKE message begins with the +/// IKE header, denoted HDR in this document. Following the header are +/// one or more IKE payloads each identified by a Next Payload field in +/// the preceding payload. Payloads are identified in the order in which +/// they appear in an IKE message by looking in the Next Payload field in +/// the IKE header, and subsequently according to the Next Payload field +/// in the IKE payload itself until a Next Payload field of zero +/// indicates that no payloads follow. If a payload of type "Encrypted" +/// is found, that payload is decrypted and its contents parsed as +/// additional payloads. An Encrypted payload MUST be the last payload +/// in a packet and an Encrypted payload MUST NOT contain another +/// Encrypted payload. +/// +/// The responder's SPI in the header identifies an instance of an IKE +/// Security Association. It is therefore possible for a single instance +/// of IKE to multiplex distinct sessions with multiple peers, including +/// multiple sessions per peer. +/// +/// All multi-octet fields representing integers are laid out in big +/// endian order (also known as "most significant byte first", or +/// "network byte order"). +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.1 +#[derive(Clone, Debug, PartialEq)] +pub struct IkeV2Header { + pub init_spi: u64, + pub resp_spi: u64, + pub next_payload: IkePayloadType, + pub maj_ver: u8, + pub min_ver: u8, + pub exch_type: IkeExchangeType, + pub flags: u8, + pub msg_id: u32, + pub length: u32, +} + +/// Payload type +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct IkePayloadType(pub u8); + +newtype_enum! { +impl debug IkePayloadType { + NoNextPayload = 0, + SecurityAssociation = 33, + KeyExchange = 34, + IdentInitiator = 35, + IdentResponder = 36, + Certificate = 37, + CertificateRequest = 38, + Authentication = 39, + Nonce = 40, + Notify = 41, + Delete = 42, + VendorID = 43, + TrafficSelectorInitiator = 44, + TrafficSelectorResponder = 45, + EncryptedAndAuthenticated = 46, + Configuration = 47, + ExtensibleAuthentication = 48, +} +} + +/// Generic (unparsed payload) +/// +/// Defined in [RFC7296] +#[derive(Debug, PartialEq)] +pub struct IkeV2GenericPayload<'a> { + pub hdr: IkeV2PayloadHeader, + pub payload: &'a [u8], +} + +/// Ciphersuite Proposal +/// +/// The Proposal structure contains within it a Proposal Num and an IPsec +/// protocol ID. Each structure MUST have a proposal number one (1) +/// greater than the previous structure. The first Proposal in the +/// initiator's SA payload MUST have a Proposal Num of one (1). One +/// reason to use multiple proposals is to propose both standard crypto +/// ciphers and combined-mode ciphers. Combined-mode ciphers include +/// both integrity and encryption in a single encryption algorithm, and +/// MUST either offer no integrity algorithm or a single integrity +/// algorithm of "NONE", with no integrity algorithm being the +/// RECOMMENDED method. If an initiator wants to propose both combined- +/// mode ciphers and normal ciphers, it must include two proposals: one +/// will have all the combined-mode ciphers, and the other will have all +/// the normal ciphers with the integrity algorithms. For example, one +/// such proposal would have two proposal structures. Proposal 1 is ESP +/// with AES-128, AES-192, and AES-256 bits in Cipher Block Chaining +/// (CBC) mode, with either HMAC-SHA1-96 or XCBC-96 as the integrity +/// algorithm; Proposal 2 is AES-128 or AES-256 in GCM mode with an +/// 8-octet Integrity Check Value (ICV). Both proposals allow but do not +/// require the use of ESNs (Extended Sequence Numbers). This can be +/// illustrated as: +/// +/// ```ignore +/// SA Payload +/// | +/// +--- Proposal #1 ( Proto ID = ESP(3), SPI size = 4, +/// | | 7 transforms, SPI = 0x052357bb ) +/// | | +/// | +-- Transform ENCR ( Name = ENCR_AES_CBC ) +/// | | +-- Attribute ( Key Length = 128 ) +/// | | +/// | +-- Transform ENCR ( Name = ENCR_AES_CBC ) +/// | | +-- Attribute ( Key Length = 192 ) +/// | | +/// | +-- Transform ENCR ( Name = ENCR_AES_CBC ) +/// | | +-- Attribute ( Key Length = 256 ) +/// | | +/// | +-- Transform INTEG ( Name = AUTH_HMAC_SHA1_96 ) +/// | +-- Transform INTEG ( Name = AUTH_AES_XCBC_96 ) +/// | +-- Transform ESN ( Name = ESNs ) +/// | +-- Transform ESN ( Name = No ESNs ) +/// | +/// +--- Proposal #2 ( Proto ID = ESP(3), SPI size = 4, +/// | 4 transforms, SPI = 0x35a1d6f2 ) +/// | +/// +-- Transform ENCR ( Name = AES-GCM with a 8 octet ICV ) +/// | +-- Attribute ( Key Length = 128 ) +/// | +/// +-- Transform ENCR ( Name = AES-GCM with a 8 octet ICV ) +/// | +-- Attribute ( Key Length = 256 ) +/// | +/// +-- Transform ESN ( Name = ESNs ) +/// +-- Transform ESN ( Name = No ESNs ) +/// ``` +/// +/// Each Proposal/Protocol structure is followed by one or more transform +/// structures. The number of different transforms is generally +/// determined by the Protocol. AH generally has two transforms: +/// Extended Sequence Numbers (ESNs) and an integrity check algorithm. +/// ESP generally has three: ESN, an encryption algorithm, and an +/// integrity check algorithm. IKE generally has four transforms: a +/// Diffie-Hellman group, an integrity check algorithm, a PRF algorithm, +/// and an encryption algorithm. For each Protocol, the set of +/// permissible transforms is assigned Transform ID numbers, which appear +/// in the header of each transform. +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.3.1 +#[derive(Clone, Debug, PartialEq)] +pub struct IkeV2Proposal<'a> { + pub last: u8, + pub reserved: u8, + pub proposal_length: u16, + pub proposal_num: u8, + pub protocol_id: ProtocolID, + pub spi_size: u8, + pub num_transforms: u8, + pub spi: Option<&'a [u8]>, + pub transforms: Vec<IkeV2RawTransform<'a>>, +} + +/// Key Exchange Payload +/// +/// The Key Exchange payload, denoted KE in this document, is used to +/// exchange Diffie-Hellman public numbers as part of a Diffie-Hellman +/// key exchange. The Key Exchange payload consists of the IKE generic +/// payload header followed by the Diffie-Hellman public value itself. +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.4 +#[derive(Debug, PartialEq)] +pub struct KeyExchangePayload<'a> { + pub dh_group: IkeTransformDHType, + pub reserved: u16, + pub kex_data: &'a [u8], +} + +/// Identification Payloads +/// +/// The Identification payloads, denoted IDi and IDr in this document, +/// allow peers to assert an identity to one another. This identity may +/// be used for policy lookup, but does not necessarily have to match +/// anything in the CERT payload; both fields may be used by an +/// implementation to perform access control decisions. When using the +/// ID_IPV4_ADDR/ID_IPV6_ADDR identity types in IDi/IDr payloads, IKEv2 +/// does not require this address to match the address in the IP header +/// of IKEv2 packets, or anything in the TSi/TSr payloads. The contents +/// of IDi/IDr are used purely to fetch the policy and authentication +/// data related to the other party. +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.5 +#[derive(Debug, PartialEq)] +pub struct IdentificationPayload<'a> { + pub id_type: IdentificationType, + pub reserved1: u8, + pub reserved2: u16, + pub ident_data: &'a [u8], +} + +/// Type of Identification +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct IdentificationType(pub u8); + +#[rustfmt::skip] +impl IdentificationType { + /// A single four (4) octet IPv4 address. + pub const ID_IPV4_ADDR : IdentificationType = IdentificationType(1); + /// A fully-qualified domain name string. An example of an ID_FQDN + /// is "example.com". The string MUST NOT contain any terminators + /// (e.g., NULL, CR, etc.). All characters in the ID_FQDN are ASCII; + /// for an "internationalized domain name", the syntax is as defined + /// in [IDNA], for example "xn--tmonesimerkki-bfbb.example.net". + pub const ID_FQDN : IdentificationType = IdentificationType(2); + /// A fully-qualified RFC 822 email address string. An example of a + /// ID_RFC822_ADDR is "jsmith@example.com". The string MUST NOT + /// contain any terminators. Because of [EAI], implementations would + /// be wise to treat this field as UTF-8 encoded text, not as + /// pure ASCII. + pub const ID_RFC822_ADDR : IdentificationType = IdentificationType(3); + /// A single sixteen (16) octet IPv6 address. + pub const ID_IPV6_ADDR : IdentificationType = IdentificationType(5); + /// The binary Distinguished Encoding Rules (DER) encoding of an ASN.1 X.500 Distinguished + /// Name. + pub const ID_DER_ASN1_DN : IdentificationType = IdentificationType(9); + /// The binary DER encoding of an ASN.1 X.509 GeneralName. + pub const ID_DER_ASN1_GN : IdentificationType = IdentificationType(10); + /// An opaque octet stream that may be used to pass vendor-specific information necessary to do + /// certain proprietary types of identification. + pub const ID_KEY_ID : IdentificationType = IdentificationType(11); +} + +/// Certificate Payload +/// +/// The Certificate payload, denoted CERT in this document, provides a +/// means to transport certificates or other authentication-related +/// information via IKE. Certificate payloads SHOULD be included in an +/// exchange if certificates are available to the sender. The Hash and +/// URL formats of the Certificate payloads should be used in case the +/// peer has indicated an ability to retrieve this information from +/// elsewhere using an HTTP_CERT_LOOKUP_SUPPORTED Notify payload. Note +/// that the term "Certificate payload" is somewhat misleading, because +/// not all authentication mechanisms use certificates and data other +/// than certificates may be passed in this payload. +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.6 +#[derive(Debug, PartialEq)] +pub struct CertificatePayload<'a> { + pub cert_encoding: CertificateEncoding, + pub cert_data: &'a [u8], +} + +/// Certificate Encoding +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.6 +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct CertificateEncoding(pub u8); + +#[allow(non_upper_case_globals)] +#[rustfmt::skip] +impl CertificateEncoding { + /// PKCS #7 wrapped X.509 certificate + pub const Pkcs7_X509 : CertificateEncoding = CertificateEncoding(1); + /// PGP Certificate + pub const PgpCert : CertificateEncoding = CertificateEncoding(2); + /// DNS Signed Key + pub const DnsKey : CertificateEncoding = CertificateEncoding(3); + /// X.509 Certificate - Signature + pub const X509Sig : CertificateEncoding = CertificateEncoding(4); + /// Kerberos Token + pub const Kerberos : CertificateEncoding = CertificateEncoding(6); + /// Certificate Revocation List (CRL) + pub const Crl : CertificateEncoding = CertificateEncoding(7); + /// Authority Revocation List (ARL) + pub const Arl : CertificateEncoding = CertificateEncoding(8); + /// SPKI Certificate + pub const SpkiCert : CertificateEncoding = CertificateEncoding(9); + /// X.509 Certificate - Attribute + pub const X509CertAttr : CertificateEncoding = CertificateEncoding(10); + /// Deprecated (was Raw RSA Key) + pub const OldRsaKey : CertificateEncoding = CertificateEncoding(11); + /// Hash and URL of X.509 certificate + pub const X509Cert_HashUrl : CertificateEncoding = CertificateEncoding(12); + /// Hash and URL of X.509 bundle + pub const X509Bundle_HashUrl : CertificateEncoding = CertificateEncoding(13); + /// OCSP Content ([RFC4806](https://tools.ietf.org/html/rfc4806)) + pub const OCSPContent : CertificateEncoding = CertificateEncoding(14); + /// Raw Public Key ([RFC7670](https://tools.ietf.org/html/rfc7670)) + pub const RawPublicKey : CertificateEncoding = CertificateEncoding(15); +} + +/// Certificate Request Payload +/// +/// The Certificate Request payload, denoted CERTREQ in this document, +/// provides a means to request preferred certificates via IKE and can +/// appear in the IKE_INIT_SA response and/or the IKE_AUTH request. +/// Certificate Request payloads MAY be included in an exchange when the +/// sender needs to get the certificate of the receiver. +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.7 +#[derive(Debug, PartialEq)] +pub struct CertificateRequestPayload<'a> { + pub cert_encoding: CertificateEncoding, + pub ca_data: &'a [u8], +} + +/// Authentication Payload +/// +/// The Authentication payload, denoted AUTH in this document, contains +/// data used for authentication purposes. +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.8 +#[derive(Debug, PartialEq)] +pub struct AuthenticationPayload<'a> { + pub auth_method: AuthenticationMethod, + pub auth_data: &'a [u8], +} + +/// Method of authentication used. +/// +/// See also [IKEV2IANA](https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml) for the latest values. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct AuthenticationMethod(pub u8); + +#[allow(non_upper_case_globals)] +#[rustfmt::skip] +impl AuthenticationMethod { + /// RSA Digital Signature + pub const RsaSig : AuthenticationMethod = AuthenticationMethod(1); + /// Shared Key Message Integrity Code + pub const SharedKeyMIC : AuthenticationMethod = AuthenticationMethod(2); + /// DSS Digital Signature + pub const DssSig : AuthenticationMethod = AuthenticationMethod(3); + /// ECDSA with SHA-256 on the P-256 curve + pub const EcdsaSha256P256 : AuthenticationMethod = AuthenticationMethod(9); + /// ECDSA with SHA-384 on the P-384 curve + pub const EcdsaSha384P384 : AuthenticationMethod = AuthenticationMethod(10); + /// ECDSA with SHA-512 on the P-512 curve + pub const EcdsaSha512P512 : AuthenticationMethod = AuthenticationMethod(11); + /// Generic Secure Password Authentication Method + pub const GenericPass : AuthenticationMethod = AuthenticationMethod(12); + /// NULL Authentication + pub const Null : AuthenticationMethod = AuthenticationMethod(13); + /// Digital Signature + pub const DigitalSig : AuthenticationMethod = AuthenticationMethod(14); + + /// Test if value is in unassigned range + pub fn is_unassigned(self) -> bool { + (self.0 >= 4 && self.0 <= 8) || + (self.0 >= 15 && self.0 <= 200) + } + + /// Test if value is in private use range + pub fn is_private_use(self) -> bool { + self.0 >= 201 + } +} + +/// Nonce Payload +/// +/// The Nonce payload, denoted as Ni and Nr in this document for the +/// initiator's and responder's nonce, respectively, contains random data used to guarantee +/// liveness during an exchange and protect against replay attacks. +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.9 +#[derive(PartialEq)] +pub struct NoncePayload<'a> { + pub nonce_data: &'a [u8], +} + +/// Notify Payload +/// +/// The Notify payload, denoted N in this document, is used to transmit informational data, such as +/// error conditions and state transitions, to an IKE peer. A Notify payload may appear in a +/// response message (usually specifying why a request was rejected), in an INFORMATIONAL exchange +/// (to report an error not in an IKE request), or in any other message to indicate sender +/// capabilities or to modify the meaning of the request. +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.10 +#[derive(PartialEq)] +pub struct NotifyPayload<'a> { + pub protocol_id: ProtocolID, + pub spi_size: u8, + pub notify_type: NotifyType, + pub spi: Option<&'a [u8]>, + pub notify_data: Option<&'a [u8]>, +} + +/// Delete Payload +/// +/// The Delete payload, denoted D in this document, contains a +/// protocol-specific Security Association identifier that the sender has +/// removed from its Security Association database and is, therefore, no +/// longer valid. Figure 17 shows the format of the Delete payload. It +/// is possible to send multiple SPIs in a Delete payload; however, each +/// SPI MUST be for the same protocol. Mixing of protocol identifiers +/// MUST NOT be performed in the Delete payload. It is permitted, +/// however, to include multiple Delete payloads in a single +/// INFORMATIONAL exchange where each Delete payload lists SPIs for a +/// different protocol. +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.11 +#[derive(Debug, PartialEq)] +pub struct DeletePayload<'a> { + pub protocol_id: ProtocolID, + pub spi_size: u8, + pub num_spi: u16, + pub spi: &'a [u8], +} + +/// Vendor ID Payload +/// +/// The Vendor ID payload, denoted V in this document, contains a vendor- +/// defined constant. The constant is used by vendors to identify and +/// recognize remote instances of their implementations. This mechanism +/// allows a vendor to experiment with new features while maintaining +/// backward compatibility. +/// +/// A Vendor ID payload MAY announce that the sender is capable of +/// accepting certain extensions to the protocol, or it MAY simply +/// identify the implementation as an aid in debugging. A Vendor ID +/// payload MUST NOT change the interpretation of any information defined +/// in this specification (i.e., the critical bit MUST be set to 0). +/// Multiple Vendor ID payloads MAY be sent. An implementation is not +/// required to send any Vendor ID payload at all. +/// +/// A Vendor ID payload may be sent as part of any message. Reception of +/// a familiar Vendor ID payload allows an implementation to make use of +/// private use numbers described throughout this document, such as +/// private payloads, private exchanges, private notifications, etc. +/// Unfamiliar Vendor IDs MUST be ignored. +/// +/// Writers of documents who wish to extend this protocol MUST define a +/// Vendor ID payload to announce the ability to implement the extension +/// in the document. It is expected that documents that gain acceptance +/// and are standardized will be given "magic numbers" out of the Future +/// Use range by IANA, and the requirement to use a Vendor ID will go +/// away. +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.12 +#[derive(Debug, PartialEq)] +pub struct VendorIDPayload<'a> { + pub vendor_id: &'a [u8], +} + +/// Type of Traffic Selector +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.13.1 +/// +/// See also [IKEV2IANA](https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml) for the latest values. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct TSType(pub u8); + +#[allow(non_upper_case_globals)] +impl TSType { + /// A range of IPv4 addresses + pub const IPv4AddrRange: TSType = TSType(7); + /// A range of IPv6 addresses + pub const IPv6AddrRange: TSType = TSType(8); + /// Fibre Channel Traffic Selectors ([RFC4595](https://tools.ietf.org/html/rfc4595)) + pub const FcAddrRange: TSType = TSType(9); +} + +/// Traffic Selector +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.13.1 +#[derive(Debug, PartialEq)] +pub struct TrafficSelector<'a> { + pub ts_type: TSType, + pub ip_proto_id: u8, + pub sel_length: u16, + pub start_port: u16, + pub end_port: u16, + pub start_addr: &'a [u8], + pub end_addr: &'a [u8], +} + +fn ipv4_from_slice(b: &[u8]) -> Ipv4Addr { + Ipv4Addr::new(b[0], b[1], b[2], b[3]) +} + +fn ipv6_from_slice(b: &[u8]) -> Ipv6Addr { + Ipv6Addr::new( + (b[0] as u16) << 8 | (b[1] as u16), + (b[2] as u16) << 8 | (b[3] as u16), + (b[4] as u16) << 8 | (b[5] as u16), + (b[6] as u16) << 8 | (b[7] as u16), + (b[8] as u16) << 8 | (b[9] as u16), + (b[10] as u16) << 8 | (b[11] as u16), + (b[12] as u16) << 8 | (b[13] as u16), + (b[14] as u16) << 8 | (b[15] as u16), + ) +} + +impl<'a> TrafficSelector<'a> { + pub fn get_ts_type(&self) -> TSType { + self.ts_type + } + + pub fn get_start_addr(&self) -> Option<IpAddr> { + match self.ts_type { + TSType::IPv4AddrRange => Some(IpAddr::V4(ipv4_from_slice(self.start_addr))), + TSType::IPv6AddrRange => Some(IpAddr::V6(ipv6_from_slice(self.start_addr))), + _ => None, + } + } + + pub fn get_end_addr(&self) -> Option<IpAddr> { + match self.ts_type { + TSType::IPv4AddrRange => Some(IpAddr::V4(ipv4_from_slice(self.end_addr))), + TSType::IPv6AddrRange => Some(IpAddr::V6(ipv6_from_slice(self.end_addr))), + _ => None, + } + } +} + +/// Traffic Selector Payload +/// +/// The Traffic Selector payload, denoted TS in this document, allows +/// peers to identify packet flows for processing by IPsec security +/// services. The Traffic Selector payload consists of the IKE generic +/// payload header followed by individual Traffic Selectors. +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.13 +#[derive(Debug, PartialEq)] +pub struct TrafficSelectorPayload<'a> { + pub num_ts: u8, + pub reserved: &'a [u8], // 3 bytes + pub ts: Vec<TrafficSelector<'a>>, +} + +/// Encrypted Payload +/// +/// The Encrypted payload, denoted SK {...} in this document, contains +/// other payloads in encrypted form. The Encrypted payload, if present +/// in a message, MUST be the last payload in the message. Often, it is +/// the only payload in the message. This payload is also called the +/// "Encrypted and Authenticated" payload. +#[derive(Debug, PartialEq)] +pub struct EncryptedPayload<'a>(pub &'a [u8]); + +/// IKE Message Payload Content +/// +/// The content of an IKE message is one of the defined payloads. +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.2 +#[derive(Debug, PartialEq)] +pub enum IkeV2PayloadContent<'a> { + SA(Vec<IkeV2Proposal<'a>>), + KE(KeyExchangePayload<'a>), + IDi(IdentificationPayload<'a>), + IDr(IdentificationPayload<'a>), + Certificate(CertificatePayload<'a>), + CertificateRequest(CertificateRequestPayload<'a>), + Authentication(AuthenticationPayload<'a>), + Nonce(NoncePayload<'a>), + Notify(NotifyPayload<'a>), + Delete(DeletePayload<'a>), + VendorID(VendorIDPayload<'a>), + TSi(TrafficSelectorPayload<'a>), + TSr(TrafficSelectorPayload<'a>), + Encrypted(EncryptedPayload<'a>), + + Unknown(&'a [u8]), + + Dummy, +} + +/// Generic Payload Header +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.2 +#[derive(Clone, Debug, PartialEq)] +pub struct IkeV2PayloadHeader { + pub next_payload_type: IkePayloadType, + pub critical: bool, + pub reserved: u8, + pub payload_length: u16, +} + +/// IKE Message Payload +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3 +#[derive(Debug, PartialEq)] +pub struct IkeV2Payload<'a> { + pub hdr: IkeV2PayloadHeader, + pub content: IkeV2PayloadContent<'a>, +} diff --git a/rust/vendor/ipsec-parser/src/ikev2_debug.rs b/rust/vendor/ipsec-parser/src/ikev2_debug.rs new file mode 100644 index 0000000..9daded4 --- /dev/null +++ b/rust/vendor/ipsec-parser/src/ikev2_debug.rs @@ -0,0 +1,68 @@ +use crate::ikev2::*; +use crate::ikev2_transforms::*; +use rusticata_macros::debug::HexSlice; +use std::fmt; + +// ------------------------- ikev2_transforms.rs ------------------------------ +// +impl<'a> fmt::Debug for IkeV2RawTransform<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + let (tf_type, tf_id) = match self.transform_type { + IkeTransformType::EncryptionAlgorithm => ( + "EncryptionAlgorithm".to_string(), + format!("{:?}", self.transform_id), + ), + IkeTransformType::PseudoRandomFunction => ( + "PseudoRandomFunction".to_string(), + format!("{:?}", self.transform_id), + ), + IkeTransformType::IntegrityAlgorithm => ( + "IntegrityAlgorithm".to_string(), + format!("{:?}", self.transform_id), + ), + IkeTransformType::DiffieHellmanGroup => ( + "DiffieHellmanGroup".to_string(), + format!("{:?}", self.transform_id), + ), + IkeTransformType::ExtendedSequenceNumbers => ( + "ExtendedSequenceNumbers".to_string(), + format!("{:?}", self.transform_id), + ), + _ => ( + format!("<Unknown transform type {}>", self.transform_type.0), + "".to_string(), + ), + }; + fmt.debug_struct("IkeV2RawTransform") + .field("last", &self.last) + .field("reserved1", &self.reserved1) + .field("transform_length", &self.transform_length) + .field("transform_type", &tf_type) + .field("reserved2", &self.reserved2) + .field("transform_id", &tf_id) + .field("attributes", &self.attributes) + .finish() + } +} + +// ------------------------- ikev2.rs ------------------------------ + +impl<'a> fmt::Debug for NoncePayload<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("NoncePayload") + .field("nonce_data", &HexSlice(self.nonce_data)) + .finish() + } +} + +impl<'a> fmt::Debug for NotifyPayload<'a> { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + fmt.debug_struct("NotifyPayload") + .field("protocol_id", &self.protocol_id) + .field("spi_size", &self.spi_size) + .field("notify_type", &self.notify_type) + .field("spi", &self.spi) + .field("notify_data", &self.notify_data.map(HexSlice)) + .finish() + } +} diff --git a/rust/vendor/ipsec-parser/src/ikev2_notify.rs b/rust/vendor/ipsec-parser/src/ikev2_notify.rs new file mode 100644 index 0000000..cef4e76 --- /dev/null +++ b/rust/vendor/ipsec-parser/src/ikev2_notify.rs @@ -0,0 +1,90 @@ +use rusticata_macros::newtype_enum; + +/// Notify Message Type +/// +/// Notification information can be error messages specifying why an SA +/// could not be established. It can also be status data that a process +/// managing an SA database wishes to communicate with a peer process. +/// +/// The table below lists the notification messages and their +/// corresponding values. The number of different error statuses was +/// greatly reduced from IKEv1 both for simplification and to avoid +/// giving configuration information to probers. +/// +/// Types in the range 0 - 16383 are intended for reporting errors. An +/// implementation receiving a Notify payload with one of these types +/// that it does not recognize in a response MUST assume that the +/// corresponding request has failed entirely. Unrecognized error types +/// in a request and status types in a request or response MUST be +/// ignored, and they should be logged. +/// +/// Notify payloads with status types MAY be added to any message and +/// MUST be ignored if not recognized. They are intended to indicate +/// capabilities, and as part of SA negotiation, are used to negotiate +/// non-cryptographic parameters. +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.10.1 +/// +/// Extensions: +/// +/// - [RFC4555](https://tools.ietf.org/html/rfc4555) IKEv2 Mobility and Multihoming Protocol (MOBIKE) +/// - [RFC4739](https://tools.ietf.org/html/rfc4739) Multiple Authentication Exchanges in the Internet Key Exchange (IKEv2) Protocol +/// - [RFC5685](https://tools.ietf.org/html/rfc5685) Redirect Mechanism for the Internet Key Exchange Protocol Version 2 (IKEv2) +/// - [RFC5723](https://tools.ietf.org/html/rfc5723) Internet Key Exchange Protocol Version 2 (IKEv2) Session Resumption +/// - [RFC7427](https://tools.ietf.org/html/rfc7427) Signature Authentication in the Internet Key Exchange Version 2 (IKEv2) +/// +/// See also [IKEV2IANA](https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml) for the latest values. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct NotifyType(pub u16); + +newtype_enum! { +impl debug NotifyType { + // error types + UNSUPPORTED_CRITICAL_PAYLOAD = 1, + INVALID_IKE_SPI = 4, + INVALID_MAJOR_VERSION = 5, + INVALID_SYNTAX = 7, + INVALID_MESSAGE_ID = 9, + INVALID_SPI = 11, + NO_PROPOSAL_CHOSEN = 14, + INVALID_KE_PAYLOAD = 17, + AUTHENTICATION_FAILED = 24, + SINGLE_PAIR_REQUIRED = 34, + NO_ADDITIONAL_SAS = 35, + INTERNAL_ADDRESS_FAILURE = 36, + FAILED_CP_REQUIRED = 37, + TS_UNACCEPTABLE = 38, + INVALID_SELECTORS = 39, + TEMPORARY_FAILURE = 43, + CHILD_SA_NOT_FOUND = 44, + // status types + INITIAL_CONTACT = 16384, + SET_WINDOW_SIZE = 16385, + ADDITIONAL_TS_POSSIBLE = 16386, + IPCOMP_SUPPORTED = 16387, + NAT_DETECTION_SOURCE_IP = 16388, + NAT_DETECTION_DESTINATION_IP = 16389, + COOKIE = 16390, + USE_TRANSPORT_MODE = 16391, + HTTP_CERT_LOOKUP_SUPPORTED = 16392, + REKEY_SA = 16393, + ESP_TFC_PADDING_NOT_SUPPORTED = 16394, + NON_FIRST_FRAGMENTS_ALSO = 16395, + // + MULTIPLE_AUTH_SUPPORTED = 16404, + ANOTHER_AUTH_FOLLOWS = 16405, + REDIRECT_SUPPORTED = 16406, + // + IKEV2_FRAGMENTATION_SUPPORTED = 16430, + SIGNATURE_HASH_ALGORITHMS = 16431, +} +} + +impl NotifyType { + pub fn is_error(self) -> bool { + self.0 < 16384 + } + pub fn is_status(self) -> bool { + self.0 > 16384 + } +} diff --git a/rust/vendor/ipsec-parser/src/ikev2_parser.rs b/rust/vendor/ipsec-parser/src/ikev2_parser.rs new file mode 100644 index 0000000..c1dd0a1 --- /dev/null +++ b/rust/vendor/ipsec-parser/src/ikev2_parser.rs @@ -0,0 +1,600 @@ +use crate::error::IPsecError; +use crate::ikev2::*; +use crate::ikev2_notify::NotifyType; +use crate::ikev2_transforms::*; +use nom::bytes::streaming::take; +use nom::combinator::{complete, cond, map, map_parser, verify}; +use nom::error::{make_error, ErrorKind}; +use nom::multi::{count, many1}; +use nom::number::streaming::{be_u16, be_u32, be_u64, be_u8}; +use nom::{Err, IResult, Needed}; + +pub fn parse_ikev2_header(i: &[u8]) -> IResult<&[u8], IkeV2Header> { + if i.len() < 28 { + return Err(Err::Incomplete(Needed::new(28))); + } + let (i, init_spi) = be_u64(i)?; + let (i, resp_spi) = be_u64(i)?; + let (i, next_payload) = map(be_u8, IkePayloadType)(i)?; + let (i, vers) = be_u8(i)?; + let maj_ver = vers >> 4; + let min_ver = vers & 0b1111; + let (i, exch_type) = map(be_u8, IkeExchangeType)(i)?; + let (i, flags) = be_u8(i)?; + let (i, msg_id) = be_u32(i)?; + let (i, length) = be_u32(i)?; + let hdr = IkeV2Header { + init_spi, + resp_spi, + next_payload, + maj_ver, + min_ver, + exch_type, + flags, + msg_id, + length, + }; + Ok((i, hdr)) +} + +#[inline] +fn bits_split_1(i: &[u8]) -> IResult<&[u8], (u8, u8)> { + let (i, b) = be_u8(i)?; + let b1 = b >> 7; + let b2_7 = b & 0b_0111_1111; + Ok((i, (b1, b2_7))) +} + +pub fn parse_ikev2_payload_generic(i: &[u8]) -> IResult<&[u8], IkeV2GenericPayload> { + let (i, next_payload_type) = map(be_u8, IkePayloadType)(i)?; + let (i, b) = bits_split_1(i)?; + let (i, payload_length) = verify(be_u16, |&n| n >= 4)(i)?; + let (i, payload) = take(payload_length - 4)(i)?; + let hdr = IkeV2PayloadHeader { + next_payload_type, + critical: b.0 == 1, + reserved: b.1, + payload_length, + }; + let payload = IkeV2GenericPayload { hdr, payload }; + Ok((i, payload)) +} + +pub fn parse_ikev2_transform(i: &[u8]) -> IResult<&[u8], IkeV2RawTransform> { + let (i, last) = be_u8(i)?; + let (i, reserved1) = be_u8(i)?; + let (i, transform_length) = be_u16(i)?; + let (i, transform_type) = be_u8(i)?; + let (i, reserved2) = be_u8(i)?; + let (i, transform_id) = be_u16(i)?; + // we have to specify a callback here to force lazy evaluation, + // because the function arguments are evaluated *before* the test (causing underflow) + let (i, attributes) = cond(transform_length > 8, |d| take(transform_length - 8)(d))(i)?; + let transform = IkeV2RawTransform { + last, + reserved1, + transform_length, + transform_type: IkeTransformType(transform_type), + reserved2, + transform_id, + attributes, + }; + Ok((i, transform)) +} + +pub fn parse_ikev2_proposal(i: &[u8]) -> IResult<&[u8], IkeV2Proposal> { + if i.len() < 8 { + return Err(Err::Incomplete(Needed::new(8))); + } + let (i, last) = be_u8(i)?; + let (i, reserved) = be_u8(i)?; + let (i, proposal_length) = be_u16(i)?; + let (i, proposal_num) = be_u8(i)?; + let (i, protocol_id) = map(be_u8, ProtocolID)(i)?; + let (i, spi_size) = be_u8(i)?; + let (i, num_transforms) = be_u8(i)?; + let (i, spi) = cond(spi_size > 0, take(spi_size))(i)?; + if proposal_length < (8u16 + spi_size as u16) { + return Err(Err::Error(make_error(i, ErrorKind::Verify))); + } + let (i, transforms) = map_parser( + take(proposal_length - 8 - (spi_size as u16)), + count(parse_ikev2_transform, num_transforms as usize), + )(i)?; + let proposal = IkeV2Proposal { + last, + reserved, + proposal_length, + proposal_num, + protocol_id, + spi_size, + num_transforms, + spi, + transforms, + }; + Ok((i, proposal)) +} + +pub fn parse_ikev2_payload_sa(i: &[u8], _length: u16) -> IResult<&[u8], IkeV2PayloadContent> { + map( + many1(complete(parse_ikev2_proposal)), + IkeV2PayloadContent::SA, + )(i) +} + +pub fn parse_ikev2_payload_kex(i: &[u8], length: u16) -> IResult<&[u8], IkeV2PayloadContent> { + if length < 4 { + return Err(Err::Error(make_error(i, ErrorKind::Verify))); + } + let (i, dh_group) = map(be_u16, IkeTransformDHType)(i)?; + let (i, reserved) = be_u16(i)?; + let (i, kex_data) = take(length - 4)(i)?; + let payload = KeyExchangePayload { + dh_group, + reserved, + kex_data, + }; + Ok((i, IkeV2PayloadContent::KE(payload))) +} + +pub fn parse_ikev2_payload_ident_init( + i: &[u8], + length: u16, +) -> IResult<&[u8], IkeV2PayloadContent> { + if length < 4 { + return Err(Err::Error(make_error(i, ErrorKind::Verify))); + } + let (i, id_type) = map(be_u8, IdentificationType)(i)?; + let (i, reserved1) = be_u8(i)?; + let (i, reserved2) = be_u16(i)?; + let (i, ident_data) = take(length - 4)(i)?; + let payload = IdentificationPayload { + id_type, + reserved1, + reserved2, + ident_data, + }; + Ok((i, IkeV2PayloadContent::IDi(payload))) +} + +pub fn parse_ikev2_payload_ident_resp( + i: &[u8], + length: u16, +) -> IResult<&[u8], IkeV2PayloadContent> { + if length < 4 { + return Err(Err::Error(make_error(i, ErrorKind::Verify))); + } + let (i, id_type) = map(be_u8, IdentificationType)(i)?; + let (i, reserved1) = be_u8(i)?; + let (i, reserved2) = be_u16(i)?; + let (i, ident_data) = take(length - 4)(i)?; + let payload = IdentificationPayload { + id_type, + reserved1, + reserved2, + ident_data, + }; + Ok((i, IkeV2PayloadContent::IDr(payload))) +} + +pub fn parse_ikev2_payload_certificate( + i: &[u8], + length: u16, +) -> IResult<&[u8], IkeV2PayloadContent> { + if length < 1 { + return Err(Err::Error(make_error(i, ErrorKind::Verify))); + } + let (i, cert_encoding) = map(be_u8, CertificateEncoding)(i)?; + let (i, cert_data) = take(length - 1)(i)?; + let payload = CertificatePayload { + cert_encoding, + cert_data, + }; + Ok((i, IkeV2PayloadContent::Certificate(payload))) +} + +pub fn parse_ikev2_payload_certificate_request( + i: &[u8], + length: u16, +) -> IResult<&[u8], IkeV2PayloadContent> { + if length < 1 { + return Err(Err::Error(make_error(i, ErrorKind::Verify))); + } + let (i, cert_encoding) = map(be_u8, CertificateEncoding)(i)?; + let (i, ca_data) = take(length - 1)(i)?; + let payload = CertificateRequestPayload { + cert_encoding, + ca_data, + }; + Ok((i, IkeV2PayloadContent::CertificateRequest(payload))) +} + +pub fn parse_ikev2_payload_authentication( + i: &[u8], + length: u16, +) -> IResult<&[u8], IkeV2PayloadContent> { + if length < 4 { + return Err(Err::Error(make_error(i, ErrorKind::Verify))); + } + let (i, auth_method) = map(be_u8, AuthenticationMethod)(i)?; + let (i, auth_data) = take(length - 4)(i)?; + let payload = AuthenticationPayload { + auth_method, + auth_data, + }; + Ok((i, IkeV2PayloadContent::Authentication(payload))) +} + +pub fn parse_ikev2_payload_nonce(i: &[u8], length: u16) -> IResult<&[u8], IkeV2PayloadContent> { + let (i, nonce_data) = take(length)(i)?; + Ok((i, IkeV2PayloadContent::Nonce(NoncePayload { nonce_data }))) +} + +pub fn parse_ikev2_payload_notify(i: &[u8], length: u16) -> IResult<&[u8], IkeV2PayloadContent> { + let (i, protocol_id) = map(be_u8, ProtocolID)(i)?; + let (i, spi_size) = be_u8(i)?; + let (i, notify_type) = map(be_u16, NotifyType)(i)?; + let (i, spi) = cond(spi_size > 0, take(spi_size))(i)?; + let (i, notify_data) = cond( + length > 8 + spi_size as u16, + // we have to specify a callback here to force lazy evaluation, + // because the function arguments are evaluated *before* the test (causing underflow) + |d| take(length - (8 + spi_size as u16))(d), + )(i)?; + let payload = NotifyPayload { + protocol_id, + spi_size, + notify_type, + spi, + notify_data, + }; + Ok((i, IkeV2PayloadContent::Notify(payload))) +} + +pub fn parse_ikev2_payload_vendor_id(i: &[u8], length: u16) -> IResult<&[u8], IkeV2PayloadContent> { + if length < 8 { + return Err(Err::Error(make_error(i, ErrorKind::Verify))); + } + let (i, vendor_id) = take(length - 8)(i)?; + Ok(( + i, + IkeV2PayloadContent::VendorID(VendorIDPayload { vendor_id }), + )) +} + +pub fn parse_ikev2_payload_delete(i: &[u8], length: u16) -> IResult<&[u8], IkeV2PayloadContent> { + if length < 8 { + return Err(Err::Error(make_error(i, ErrorKind::Verify))); + } + let (i, protocol_id) = map(be_u8, ProtocolID)(i)?; + let (i, spi_size) = be_u8(i)?; + let (i, num_spi) = be_u16(i)?; + let (i, spi) = take(length - 8)(i)?; + let payload = DeletePayload { + protocol_id, + spi_size, + num_spi, + spi, + }; + Ok((i, IkeV2PayloadContent::Delete(payload))) +} + +fn parse_ts_addr(i: &[u8], t: TSType) -> IResult<&[u8], &[u8]> { + match t { + TSType::IPv4AddrRange => take(4usize)(i), + TSType::IPv6AddrRange => take(16usize)(i), + _ => Err(nom::Err::Error(make_error(i, ErrorKind::Switch))), + } +} + +fn parse_ikev2_ts(i: &[u8]) -> IResult<&[u8], TrafficSelector> { + let (i, ts_type) = map(be_u8, TSType)(i)?; + let (i, ip_proto_id) = be_u8(i)?; + let (i, sel_length) = be_u16(i)?; + let (i, start_port) = be_u16(i)?; + let (i, end_port) = be_u16(i)?; + let (i, start_addr) = parse_ts_addr(i, ts_type)?; + let (i, end_addr) = parse_ts_addr(i, ts_type)?; + let ts = TrafficSelector { + ts_type, + ip_proto_id, + sel_length, + start_port, + end_port, + start_addr, + end_addr, + }; + Ok((i, ts)) +} + +pub fn parse_ikev2_payload_ts(i: &[u8], length: u16) -> IResult<&[u8], TrafficSelectorPayload> { + if length < 4 { + return Err(Err::Error(make_error(i, ErrorKind::Verify))); + } + let (i, num_ts) = be_u8(i)?; + let (i, reserved) = take(3usize)(i)?; + let (i, ts) = map_parser(take(length - 4), many1(complete(parse_ikev2_ts)))(i)?; + let payload = TrafficSelectorPayload { + num_ts, + reserved, + ts, + }; + Ok((i, payload)) +} + +pub fn parse_ikev2_payload_ts_init(i: &[u8], length: u16) -> IResult<&[u8], IkeV2PayloadContent> { + map( + |d| parse_ikev2_payload_ts(d, length), + IkeV2PayloadContent::TSi, + )(i) +} + +pub fn parse_ikev2_payload_ts_resp(i: &[u8], length: u16) -> IResult<&[u8], IkeV2PayloadContent> { + map( + |d| parse_ikev2_payload_ts(d, length), + IkeV2PayloadContent::TSr, + )(i) +} + +pub fn parse_ikev2_payload_encrypted(i: &[u8], length: u16) -> IResult<&[u8], IkeV2PayloadContent> { + map(take(length), |d| { + IkeV2PayloadContent::Encrypted(EncryptedPayload(d)) + })(i) +} + +pub fn parse_ikev2_payload_unknown(i: &[u8], length: u16) -> IResult<&[u8], IkeV2PayloadContent> { + map(take(length), IkeV2PayloadContent::Unknown)(i) +} + +#[rustfmt::skip] +pub fn parse_ikev2_payload_with_type( + i: &[u8], + length: u16, + next_payload_type: IkePayloadType, +) -> IResult<&[u8], IkeV2PayloadContent> { + let f = match next_payload_type { + // IkePayloadType::NoNextPayload => parse_ikev2_payload_unknown, // XXX ? + IkePayloadType::SecurityAssociation => parse_ikev2_payload_sa, + IkePayloadType::KeyExchange => parse_ikev2_payload_kex, + IkePayloadType::IdentInitiator => parse_ikev2_payload_ident_init, + IkePayloadType::IdentResponder => parse_ikev2_payload_ident_resp, + IkePayloadType::Certificate => parse_ikev2_payload_certificate, + IkePayloadType::CertificateRequest => parse_ikev2_payload_certificate_request, + IkePayloadType::Authentication => parse_ikev2_payload_authentication, + IkePayloadType::Nonce => parse_ikev2_payload_nonce, + IkePayloadType::Notify => parse_ikev2_payload_notify, + IkePayloadType::Delete => parse_ikev2_payload_delete, + IkePayloadType::VendorID => parse_ikev2_payload_vendor_id, + IkePayloadType::TrafficSelectorInitiator => parse_ikev2_payload_ts_init, + IkePayloadType::TrafficSelectorResponder => parse_ikev2_payload_ts_resp, + IkePayloadType::EncryptedAndAuthenticated => parse_ikev2_payload_encrypted, + // None => parse_ikev2_payload_unknown, + _ => parse_ikev2_payload_unknown, + // _ => panic!("unknown type {}",next_payload_type), + }; + map_parser(take(length),move |d| f(d, length))(i) +} + +fn parse_ikev2_payload_list_fold<'a>( + res_v: Result<Vec<IkeV2Payload<'a>>, IPsecError>, + p: IkeV2GenericPayload<'a>, +) -> Result<Vec<IkeV2Payload<'a>>, IPsecError> { + let mut v = res_v?; + // println!("parse_payload_list_fold: v.len={} p={:?}",v.len(),p); + debug_assert!(!v.is_empty()); + let last_payload = v + .last() + .expect("parse_payload_list_fold: called with empty input"); + let next_payload_type = last_payload.hdr.next_payload_type; + if p.hdr.payload_length < 4 { + return Err(IPsecError::PayloadTooSmall); + } + match parse_ikev2_payload_with_type(p.payload, p.hdr.payload_length - 4, next_payload_type) { + Ok((rem, p2)) => { + // let (rem, p2) = parse_ikev2_payload_with_type(p.payload, p.hdr.payload_length - 4, next_payload_type)?; + if !rem.is_empty() { + return Err(IPsecError::ExtraBytesInPayload); // XXX should this be only a warning? + } + let payload = IkeV2Payload { + hdr: p.hdr.clone(), + content: p2, + }; + v.push(payload); + Ok(v) + } + Err(nom::Err::Error(e)) | Err(nom::Err::Failure(e)) => Err(IPsecError::NomError(e.code)), + Err(nom::Err::Incomplete(_)) => Err(IPsecError::NomError(ErrorKind::Complete)), + } +} + +pub fn parse_ikev2_payload_list( + i: &[u8], + initial_type: IkePayloadType, +) -> IResult<&[u8], Result<Vec<IkeV2Payload>, IPsecError>> { + // XXX fold manually, because fold_many1 requires accumulator to have Clone, and we don't want + // XXX to implement that for IkeV2Payload + let mut acc = Ok(vec![IkeV2Payload { + hdr: IkeV2PayloadHeader { + next_payload_type: initial_type, + critical: false, + reserved: 0, + payload_length: 0, + }, + content: IkeV2PayloadContent::Dummy, + }]); + #[allow(clippy::clone_double_ref)] + let mut i = i.clone(); + loop { + if i.is_empty() { + break; + } + + let (rem, p) = complete(parse_ikev2_payload_generic)(i)?; + + acc = parse_ikev2_payload_list_fold(acc, p); + + i = rem; + } + Ok((i, acc)) + // XXX should we split_first() the vector and return all but the first element ? +} + +/// Parse an IKEv2 message +/// +/// Parse the IKEv2 header and payload list +#[allow(clippy::type_complexity)] +pub fn parse_ikev2_message( + i: &[u8], +) -> IResult<&[u8], (IkeV2Header, Result<Vec<IkeV2Payload>, IPsecError>)> { + let (i, hdr) = parse_ikev2_header(i)?; + if hdr.length < 28 { + return Err(Err::Error(make_error(i, ErrorKind::Verify))); + } + let (i, msg) = map_parser(take(hdr.length - 28), |d| { + parse_ikev2_payload_list(d, hdr.next_payload) + })(i)?; + Ok((i, (hdr, msg))) +} + +#[cfg(test)] +mod tests { + use crate::ikev2_parser::*; + + #[rustfmt::skip] +static IKEV2_INIT_REQ: &[u8] = &[ + 0x01, 0xf8, 0xc3, 0xd4, 0xbb, 0x77, 0x3f, 0x2f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x21, 0x20, 0x22, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x48, 0x22, 0x00, 0x00, 0x30, + 0x00, 0x00, 0x00, 0x2c, 0x01, 0x01, 0x00, 0x04, 0x03, 0x00, 0x00, 0x0c, 0x01, 0x00, 0x00, 0x14, + 0x80, 0x0e, 0x00, 0x80, 0x03, 0x00, 0x00, 0x08, 0x03, 0x00, 0x00, 0x0c, 0x03, 0x00, 0x00, 0x08, + 0x02, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x1e, 0x28, 0x00, 0x00, 0x88, + 0x00, 0x1e, 0x00, 0x00, 0x8f, 0xe6, 0xf3, 0x6e, 0x88, 0x7b, 0x18, 0x9b, 0x5e, 0xce, 0xf2, 0x56, + 0xf9, 0x8d, 0x76, 0xaa, 0xcb, 0x07, 0xb3, 0xb9, 0x58, 0xee, 0x73, 0xea, 0x7b, 0x73, 0xb1, 0x04, + 0x7e, 0xa4, 0x2a, 0x4e, 0x44, 0x1f, 0xb9, 0x3e, 0xf9, 0xa9, 0xab, 0x0c, 0x54, 0x5a, 0xa7, 0x46, + 0x2e, 0x58, 0x3c, 0x06, 0xb2, 0xed, 0x91, 0x8d, 0x11, 0xca, 0x67, 0xdb, 0x21, 0x6b, 0xb8, 0xad, + 0xbf, 0x57, 0x3f, 0xba, 0x5a, 0xa6, 0x7d, 0x49, 0x83, 0x4b, 0xa9, 0x93, 0x6f, 0x4c, 0xe9, 0x66, + 0xcd, 0x57, 0x5c, 0xba, 0x07, 0x42, 0xfa, 0x0b, 0xe8, 0xb9, 0xd0, 0x25, 0xc4, 0xb9, 0xdf, 0x29, + 0xd7, 0xe4, 0x6e, 0xd6, 0x54, 0x78, 0xaa, 0x95, 0x02, 0xbf, 0x25, 0x55, 0x71, 0xfa, 0x9e, 0xcb, + 0x05, 0xea, 0x8f, 0x7b, 0x14, 0x0e, 0x1d, 0xdf, 0xb4, 0x03, 0x5f, 0x2d, 0x21, 0x66, 0x58, 0x6e, + 0x42, 0x72, 0x32, 0x03, 0x29, 0x00, 0x00, 0x24, 0xe3, 0x3b, 0x52, 0xaa, 0x6f, 0x6d, 0x62, 0x87, + 0x16, 0xd7, 0xab, 0xc6, 0x45, 0xa6, 0xcc, 0x97, 0x07, 0x43, 0x3d, 0x85, 0x83, 0xde, 0xab, 0x97, + 0xdb, 0xbf, 0x08, 0xce, 0x0f, 0xad, 0x59, 0x71, 0x29, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x40, 0x04, + 0xcc, 0xc0, 0x64, 0x5c, 0x1e, 0xeb, 0xc2, 0x1d, 0x09, 0x2b, 0xf0, 0x7f, 0xca, 0x34, 0xc3, 0xe6, + 0x2b, 0x20, 0xec, 0x8f, 0x29, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x40, 0x05, 0x15, 0x39, 0x75, 0x77, + 0xf5, 0x54, 0x87, 0xa3, 0x8f, 0xd8, 0xaf, 0x70, 0xb0, 0x9c, 0x20, 0x9c, 0xff, 0x4a, 0x37, 0xd1, + 0x29, 0x00, 0x00, 0x10, 0x00, 0x00, 0x40, 0x2f, 0x00, 0x01, 0x00, 0x02, 0x00, 0x03, 0x00, 0x04, + 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x40, 0x16 +]; + + #[test] + fn test_ikev2_init_req() { + let empty = &b""[..]; + let bytes = &IKEV2_INIT_REQ[0..28]; + let expected = Ok(( + empty, + IkeV2Header { + init_spi: 0x01f8c3d4bb773f2f, + resp_spi: 0x0, + next_payload: IkePayloadType::SecurityAssociation, + maj_ver: 2, + min_ver: 0, + exch_type: IkeExchangeType::IKE_SA_INIT, + flags: 0x8, + msg_id: 0, + length: 328, + }, + )); + let res = parse_ikev2_header(bytes); + assert_eq!(res, expected); + } + + static IKEV2_INIT_RESP: &[u8] = include_bytes!("../assets/ike-sa-init-resp.bin"); + + #[test] + fn test_ikev2_init_resp() { + let bytes = IKEV2_INIT_RESP; + let (rem, ref hdr) = parse_ikev2_header(bytes).expect("parsing header failed"); + let (rem2, res_p) = + parse_ikev2_payload_list(rem, hdr.next_payload).expect("parsing payload failed"); + assert!(rem2.is_empty()); + let p = res_p.expect("parsing payload failed"); + // there are 5 items + dummy => 6 + assert_eq!(p.len(), 6); + // first one is always dummy + assert_eq!(p[0].content, IkeV2PayloadContent::Dummy); + } + + #[rustfmt::skip] +static IKEV2_PAYLOAD_SA: &[u8] = &[ + 0x22, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x24, 0x01, 0x01, 0x00, 0x03, 0x03, 0x00, 0x00, 0x0c, + 0x01, 0x00, 0x00, 0x14, 0x80, 0x0e, 0x00, 0x80, 0x03, 0x00, 0x00, 0x08, 0x02, 0x00, 0x00, 0x05, + 0x00, 0x00, 0x00, 0x08, 0x04, 0x00, 0x00, 0x1e +]; + + #[test] + fn test_ikev2_payload_sa() { + let bytes = IKEV2_PAYLOAD_SA; + let expected1 = IkeV2GenericPayload { + hdr: IkeV2PayloadHeader { + next_payload_type: IkePayloadType::KeyExchange, + critical: false, + reserved: 0, + payload_length: 40, + }, + payload: &bytes[4..], + }; + let (_, res) = parse_ikev2_payload_generic(bytes).expect("Failed to parse"); + assert_eq!(res, expected1); + let attrs1 = &[0x80, 0x0e, 0x00, 0x80]; + let expected2 = IkeV2PayloadContent::SA(vec![IkeV2Proposal { + last: 0, + reserved: 0, + proposal_length: 36, + proposal_num: 1, + protocol_id: ProtocolID::IKE, + spi_size: 0, + num_transforms: 3, + spi: None, + transforms: vec![ + IkeV2RawTransform { + last: 3, + reserved1: 0, + transform_length: 12, + transform_type: IkeTransformType::EncryptionAlgorithm, + reserved2: 0, + transform_id: 20, + attributes: Some(attrs1), + }, + IkeV2RawTransform { + last: 3, + reserved1: 0, + transform_length: 8, + transform_type: IkeTransformType::PseudoRandomFunction, + reserved2: 0, + transform_id: 5, + attributes: None, + }, + IkeV2RawTransform { + last: 0, + reserved1: 0, + transform_length: 8, + transform_type: IkeTransformType::DiffieHellmanGroup, + reserved2: 0, + transform_id: 30, + attributes: None, + }, + ], + }]); + + let (rem, res2) = parse_ikev2_payload_sa(res.payload, 0).expect("Failed to parse"); + assert!(rem.is_empty()); + assert_eq!(res2, expected2); + } + + #[test] + fn test_ikev2_parse_payload_many() { + // let empty = &b""[..]; + let bytes = &IKEV2_INIT_REQ[28..]; + let res = parse_ikev2_payload_list(bytes, IkePayloadType::SecurityAssociation); + println!("{:?}", res); + } +} diff --git a/rust/vendor/ipsec-parser/src/ikev2_transforms.rs b/rust/vendor/ipsec-parser/src/ikev2_transforms.rs new file mode 100644 index 0000000..589c996 --- /dev/null +++ b/rust/vendor/ipsec-parser/src/ikev2_transforms.rs @@ -0,0 +1,271 @@ +use rusticata_macros::newtype_enum; +use std::convert::From; + +/// Transform (cryptographic algorithm) type +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.3.2 +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct IkeTransformType(pub u8); + +newtype_enum! { +impl debug IkeTransformType { + EncryptionAlgorithm = 1, + PseudoRandomFunction = 2, + IntegrityAlgorithm = 3, + DiffieHellmanGroup = 4, + ExtendedSequenceNumbers = 5, +} +} + +/// Encryption values +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.3.2 +/// +/// See also [IKEV2IANA](https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml) for the latest values. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct IkeTransformEncType(pub u16); + +newtype_enum! { +impl debug IkeTransformEncType { + // 0 is reserved + ENCR_DES_IV64 = 1, + ENCR_DES = 2, + ENCR_3DES = 3, + ENCR_RC5 = 4, + ENCR_IDEA = 5, + ENCR_CAST = 6, + ENCR_BLOWFISH = 7, + ENCR_3IDEA = 8, + ENCR_DES_IV32 = 9, + // 10 is reserved + ENCR_NULL = 11, + ENCR_AES_CBC = 12, + ENCR_AES_CTR = 13, + ENCR_AES_CCM_8 = 14, + ENCR_AES_CCM_12 = 15, + ENCR_AES_CCM_16 = 16, + // 17 is unassigned + ENCR_AES_GCM_8 = 18, + ENCR_AES_GCM_12 = 19, + ENCR_AES_GCM_16 = 20, + ENCR_NULL_AUTH_AES_GMAC = 21, + // 22 is reserved for IEEE P1619 XTS-AES + ENCR_CAMELLIA_CBC = 23, + ENCR_CAMELLIA_CTR = 24, + ENCR_CAMELLIA_CCM_8 = 25, + ENCR_CAMELLIA_CCM_12 = 26, + ENCR_CAMELLIA_CCM_16 = 27, + ENCR_CHACHA20_POLY1305 = 28, // [RFC7634] +} +} + +impl IkeTransformEncType { + pub fn is_aead(self) -> bool { + matches!( + self, + IkeTransformEncType::ENCR_AES_CCM_8 + | IkeTransformEncType::ENCR_AES_CCM_12 + | IkeTransformEncType::ENCR_AES_CCM_16 + | IkeTransformEncType::ENCR_AES_GCM_8 + | IkeTransformEncType::ENCR_AES_GCM_12 + | IkeTransformEncType::ENCR_AES_GCM_16 + | IkeTransformEncType::ENCR_CAMELLIA_CCM_8 + | IkeTransformEncType::ENCR_CAMELLIA_CCM_12 + | IkeTransformEncType::ENCR_CAMELLIA_CCM_16 + | IkeTransformEncType::ENCR_CHACHA20_POLY1305 + ) + } + + pub fn is_unassigned(self) -> bool { + self.0 >= 23 && self.0 <= 1023 + } + pub fn is_private_use(self) -> bool { + self.0 >= 1024 + } +} + +/// Pseudo-Random Function values +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.3.2 +/// +/// See also [IKEV2IANA](https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml) for the latest values. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct IkeTransformPRFType(pub u16); + +newtype_enum! { +impl debug IkeTransformPRFType { + PRF_NULL = 0, + PRF_HMAC_MD5 = 1, + PRF_HMAC_SHA1 = 2, + PRF_HMAC_TIGER = 3, + PRF_AES128_XCBC = 4, + PRF_HMAC_SHA2_256 = 5, + PRF_HMAC_SHA2_384 = 6, + PRF_HMAC_SHA2_512 = 7, + PRF_AES128_CMAC = 8, +} +} + +impl IkeTransformPRFType { + pub fn is_unassigned(self) -> bool { + self.0 >= 9 && self.0 <= 1023 + } + pub fn is_private_use(self) -> bool { + self.0 >= 1024 + } +} + +/// Authentication / Integrity values +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.3.2 +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct IkeTransformAuthType(pub u16); + +newtype_enum! { +impl debug IkeTransformAuthType { + NONE = 0, + AUTH_HMAC_MD5_96 = 1, + AUTH_HMAC_SHA1_96 = 2, + AUTH_DES_MAC = 3, + AUTH_KPDK_MD5 = 4, + AUTH_AES_XCBC_96 = 5, + AUTH_HMAC_MD5_128 = 6, + AUTH_HMAC_SHA1_160 = 7, + AUTH_AES_CMAC_96 = 8, + AUTH_AES_128_GMAC = 9, + AUTH_AES_192_GMAC = 10, + AUTH_AES_256_GMAC = 11, + AUTH_HMAC_SHA2_256_128 = 12, + AUTH_HMAC_SHA2_384_192 = 13, + AUTH_HMAC_SHA2_512_256 = 14, +} +} + +impl IkeTransformAuthType { + pub fn is_unassigned(self) -> bool { + self.0 >= 15 && self.0 <= 1023 + } + pub fn is_private_use(self) -> bool { + self.0 >= 1024 + } +} + +/// Diffie-Hellman values +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.3.2 +/// +/// See also [IKEV2IANA](https://www.iana.org/assignments/ikev2-parameters/ikev2-parameters.xhtml) for the latest values. +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct IkeTransformDHType(pub u16); + +newtype_enum! { +impl debug IkeTransformDHType { + None = 0, + Modp768 = 1, + Modp1024 = 2, + Modp1536 = 5, + Modp2048 = 14, + Modp3072 = 15, + Modp4096 = 16, + Modp6144 = 17, + Modp8192 = 18, + Ecp256 = 19, + Ecp384 = 20, + Ecp521 = 21, + Modp1024s160 = 22, + Modp2048s224 = 23, + Modp2048s256 = 24, + Ecp192 = 25, + Ecp224 = 26, + BrainpoolP224r1 = 27, + BrainpoolP256r1 = 28, + BrainpoolP384r1 = 29, + BrainpoolP512r1 = 30, + Curve25519 = 31, + Curve448 = 32, +} +} + +impl IkeTransformDHType { + pub fn is_unassigned(self) -> bool { + self.0 >= 15 && self.0 <= 1023 + } + pub fn is_private_use(self) -> bool { + self.0 >= 1024 + } +} + +/// Extended Sequence Number values +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.3.2 +#[derive(Clone, Copy, PartialEq, Eq)] +pub struct IkeTransformESNType(pub u16); + +newtype_enum! { +impl debug IkeTransformESNType { + NoESN = 0, + ESN = 1, +} +} + +/// Raw representation of a transform (cryptographic algorithm) and parameters +/// +/// Use the `From` method to convert it to a [`IkeV2Transform`](enum.IkeV2Transform.html) +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.3 +#[derive(Clone, PartialEq)] +pub struct IkeV2RawTransform<'a> { + pub last: u8, + pub reserved1: u8, + pub transform_length: u16, + pub transform_type: IkeTransformType, + pub reserved2: u8, + pub transform_id: u16, + pub attributes: Option<&'a [u8]>, +} + +/// IKEv2 Transform (cryptographic algorithm) +/// +/// This structure is a simple representation of a transform, containing only the type (encryption, +/// etc.). To store the parameters, use [`IkeV2RawTransform`](struct.IkeV2RawTransform.html). +/// +/// Defined in [RFC7296](https://tools.ietf.org/html/rfc7296) section 3.3 +#[derive(Debug, PartialEq)] +pub enum IkeV2Transform { + Encryption(IkeTransformEncType), + PRF(IkeTransformPRFType), + Auth(IkeTransformAuthType), + DH(IkeTransformDHType), + ESN(IkeTransformESNType), + /// Unknown tranform (type,id) + Unknown(IkeTransformType, u16), +} + +impl<'a> From<&'a IkeV2RawTransform<'a>> for IkeV2Transform { + fn from(r: &IkeV2RawTransform) -> IkeV2Transform { + match r.transform_type { + IkeTransformType::EncryptionAlgorithm => { + IkeV2Transform::Encryption(IkeTransformEncType(r.transform_id)) + } + IkeTransformType::PseudoRandomFunction => { + IkeV2Transform::PRF(IkeTransformPRFType(r.transform_id)) + } + IkeTransformType::IntegrityAlgorithm => { + IkeV2Transform::Auth(IkeTransformAuthType(r.transform_id)) + } + IkeTransformType::DiffieHellmanGroup => { + IkeV2Transform::DH(IkeTransformDHType(r.transform_id)) + } + IkeTransformType::ExtendedSequenceNumbers => { + IkeV2Transform::ESN(IkeTransformESNType(r.transform_id)) + } + _ => IkeV2Transform::Unknown(r.transform_type, r.transform_id), + } + } +} + +impl<'a> From<IkeV2RawTransform<'a>> for IkeV2Transform { + fn from(r: IkeV2RawTransform) -> IkeV2Transform { + (&r).into() + } +} diff --git a/rust/vendor/ipsec-parser/src/lib.rs b/rust/vendor/ipsec-parser/src/lib.rs new file mode 100644 index 0000000..62ddac6 --- /dev/null +++ b/rust/vendor/ipsec-parser/src/lib.rs @@ -0,0 +1,80 @@ +//! # IPsec parsers +//! +//! This crate contains several parsers using for IPsec: IKEv2, and reading the envelope of ESP +//! encapsulated messages. +//! This parser provides the base functions to read and analyze messages, but does not handle the +//! interpretation of messages. +//! +//! ESP is supported, but only to read the envelope of the payload. +//! +//! Encapsulated ESP is supported, to differentiate between IKE and ESP headers. +//! +//! # IKEv2 parser +//! +//! An IKEv2 (RFC7296) parser, implemented with the [nom](https://github.com/Geal/nom) +//! parser combinator framework. +//! +//! The code is available on [Github](https://github.com/rusticata/ipsec-parser) +//! and is part of the [Rusticata](https://github.com/rusticata) project. +//! +//! To parse an IKE packet, first read the header using `parse_ikev2_header`, then use the type +//! from the header to parse the remaining part: +//! +//! +//! ```rust +//! # extern crate nom; +//! # extern crate ipsec_parser; +//! use ipsec_parser::*; +//! use nom::IResult; +//! +//! static IKEV2_INIT_RESP: &'static [u8] = include_bytes!("../assets/ike-sa-init-resp.bin"); +//! +//! # fn main() { +//! fn test_ikev2_init_resp() { +//! let bytes = IKEV2_INIT_RESP; +//! match parse_ikev2_header(&bytes) { +//! Ok( (rem, ref hdr) ) => { +//! match parse_ikev2_payload_list(rem,hdr.next_payload) { +//! Ok( (_, Ok(ref p)) ) => { +//! // p is a list of payloads +//! // first one is always dummy +//! assert!(p.len() > 0); +//! assert_eq!(p[0].content, IkeV2PayloadContent::Dummy); +//! for payload in p { +//! match payload.content { +//! IkeV2PayloadContent::SA(ref sa) => { /* .. */ }, +//! _ => () +//! } +//! } +//! }, +//! e => { eprintln!("Parsing payload failed: {:?}", e); }, +//! } +//! }, +//! _ => { eprintln!("Parsing header failed"); }, +//! } +//! } +//! # } +//! ``` + +#![deny(/*missing_docs,*/ + unstable_features, + unused_import_braces, unused_qualifications)] +#![forbid(unsafe_code)] + +mod error; +mod esp; +mod ikev2; +mod ikev2_debug; +mod ikev2_notify; +mod ikev2_parser; +mod ikev2_transforms; +pub use error::*; +pub use esp::*; +pub use ikev2::*; +pub use ikev2_debug::*; +pub use ikev2_notify::*; +pub use ikev2_parser::*; +pub use ikev2_transforms::*; + +// re-export modules +pub use nom; |