diff --git a/Cargo.lock b/Cargo.lock index d415e5d03..1c5802e1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -106,6 +106,18 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + [[package]] name = "block-buffer" version = "0.10.3" @@ -358,6 +370,14 @@ dependencies = [ "typenum", ] +[[package]] +name = "cryptopan" +version = "0.1.0-pre" +dependencies = [ + "aes", + "bitvec", +] + [[package]] name = "der" version = "0.7.0-pre" @@ -441,6 +461,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + [[package]] name = "futures" version = "0.3.25" @@ -927,6 +953,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + [[package]] name = "rand" version = "0.8.5" @@ -1271,6 +1303,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + [[package]] name = "tempfile" version = "3.3.0" @@ -1506,6 +1544,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + [[package]] name = "x509-cert" version = "0.2.0-pre" diff --git a/Cargo.toml b/Cargo.toml index 11b73a7df..197cbacc0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ members = [ "cms", "const-oid", "crmf", + "cryptopan", "der", "der/derive", "pem-rfc7468", diff --git a/cryptopan/CHANGELOG.md b/cryptopan/CHANGELOG.md new file mode 100644 index 000000000..2d91ab974 --- /dev/null +++ b/cryptopan/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning]https://semver.org/spec/v2.0.0.html + +# 0.1.0-PRE (2022-01-23) +## Added +Initial Version \ No newline at end of file diff --git a/cryptopan/Cargo.toml b/cryptopan/Cargo.toml new file mode 100644 index 000000000..31f8e2005 --- /dev/null +++ b/cryptopan/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "cryptopan" +version = "0.1.0-pre" +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" +description = "CryptoPAN algorithm for IP address anonymization" +repository = "https://github.com/RustCrypto/utils" +keywords = ["cryptopan", "ip anonymization"] +categories = ["cryptography"] +edition = "2021" +readme = "README.md" + +[dependencies] +aes="0.8.1" +bitvec = "1.0.1" diff --git a/cryptopan/LICENSE-APACHE b/cryptopan/LICENSE-APACHE new file mode 100644 index 000000000..b056c0fc6 --- /dev/null +++ b/cryptopan/LICENSE-APACHE @@ -0,0 +1,206 @@ +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. +Footer +© 2023 GitHub, Inc. +Footer navigation +Terms +Privacy diff --git a/cryptopan/LICENSE-MIT b/cryptopan/LICENSE-MIT new file mode 100644 index 000000000..f86aed1b7 --- /dev/null +++ b/cryptopan/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2018-2023 The RustCrypto Project Developers + +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. \ No newline at end of file diff --git a/cryptopan/README.md b/cryptopan/README.md new file mode 100644 index 000000000..004c71f5a --- /dev/null +++ b/cryptopan/README.md @@ -0,0 +1,37 @@ +# [RustCrypto]: CryptoPAN IP Address Anonymization + +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] + +Anonymizes IP addresses using the CryptoPAN algorithm tightly based on the GO implementation by Yawning Angel (https://github.com/Yawning/cryptopan), which is based on the original reference implementation [paper by J. Fan, J. Xu, M. Ammar, and S. Moon. (https://ieeexplore.ieee.org/abstract/document/1181415)] + +CryptoPAN is a prefix-preserving, 1-1 mapping algorithm that allows for consistent anonymization of IP addresses across datasets, provided that the same 256-bit key is used. + +IPv6 anonymization is supported, but it is not known if the code conforms to the reference implementation. + +## License + +Licensed under either of: + + * [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) + * [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.41+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260052-utils + +[//]: # (general links) + +[RustCrypto]: https://github.com/rustcrypto + + diff --git a/cryptopan/src/lib.rs b/cryptopan/src/lib.rs new file mode 100644 index 000000000..5522756ca --- /dev/null +++ b/cryptopan/src/lib.rs @@ -0,0 +1,120 @@ +//! `CryptoPAn` +//! Anonymizes IP addresses using the CryptoPAN algorithm tightly based on the +//! GO implementation by Yawning Angel [Repo](https://github.com/Yawning/cryptopan), which is based on the +//! original reference implementation [paper by J. Fan, J. Xu, M. Ammar, and S. Moon. +//! [Paper](https://ieeexplore.ieee.org/abstract/document/1181415)] +//! +//! Instantiate using a 256-bit key using `new` or `new_from_slice` and use `encrypt_v4` and `encrypt_v6` to encrypt IP addresses +//! ``` +//! use cryptopan::CryptoPAn; +//! use std::net::{Ipv4Addr,Ipv6Addr}; +//! use std::str::FromStr; +//! use aes::cipher::KeyInit; +//! +//! const KEY: [u8; 32] = [21, 34, 23, 141, 51, 164, 207, 128, 19, 10, 91, 22, 73, 144, 125, 16, +//! 216, 152, 143, 131, 121,121, 101, 39, 98, 87, 76, 45, 42, 132, 34, 2,]; +//! +//! let cp = CryptoPAn::new(&KEY.into()); +//! cp.encrypt_v4(Ipv4Addr::from_str("127.0.0.1").unwrap()); +//! cp.encrypt_v6(Ipv6Addr::from_str("2001:db8::1").unwrap()); +//! ``` +use aes::cipher::BlockEncryptMut; +use aes::cipher::{generic_array::GenericArray, InvalidLength, KeyInit}; +use bitvec::field::BitField; +use bitvec::prelude::Msb0; +use bitvec::{bitvec, vec::BitVec}; +use std::net::{Ipv4Addr, Ipv6Addr}; + +/// The CryptoPAN anonymizer +pub struct CryptoPAn { + aes: aes::Aes128, + pad: BitVec, +} + +// Enforce a 256-bit key for `new` +impl aes::cipher::KeySizeUser for CryptoPAn { + type KeySize = aes::cipher::consts::U32; +} + +impl KeyInit for CryptoPAn { + /// Creates new CryptoPAn using 256-bit key + fn new(key: &aes::cipher::Key) -> Self { + // Create new AES128 encryptor with first 128 bits + let mut aes = aes::Aes128::new(GenericArray::from_slice(&key[0..16])); + + // Encrypt the second 128 bits + let mut buf = GenericArray::clone_from_slice(&key[16..32]); + aes.encrypt_block_mut(&mut buf); + + Self { + aes, + pad: BitVec::from_slice(buf.as_slice()), + } + } + + /// Creates new CryptoPAn from a generic slice + fn new_from_slice(key: &[u8]) -> Result { + match key.len() { + 32 => Ok(Self::new(key.into())), + _ => Err(InvalidLength), + } + } +} + +impl CryptoPAn { + /// Encrypts an IpV4Addr + pub fn encrypt_v4(&self, input: Ipv4Addr) -> Ipv4Addr { + let bits = self.encrypt(BitVec::::from_slice(&input.octets())); + let bs = bits.as_bitslice(); + Ipv4Addr::new( + bs[0..8].load::(), + bs[8..16].load::(), + bs[16..24].load::(), + bs[24..32].load::(), + ) + } + + /// Encrypts an IpV6Addr + pub fn encrypt_v6(&self, input: Ipv6Addr) -> Ipv6Addr { + let bits = self.encrypt(BitVec::::from_slice(&input.octets())); + let bs = bits.as_bitslice(); + Ipv6Addr::new( + bs[0..16].load::(), + bs[16..32].load::(), + bs[32..48].load::(), + bs[48..64].load::(), + bs[64..80].load::(), + bs[80..96].load::(), + bs[96..112].load::(), + bs[112..128].load::(), + ) + } + + /// Encrypts an IP address using the key + fn encrypt(&self, bits: BitVec) -> BitVec { + let mut encrypted = bitvec!(u8, Msb0; 0; bits.len()); + let mut padding = self.pad.clone(); + + // The first bit does not take from the original address + let mut encpadding = padding.clone(); + let g = GenericArray::from_mut_slice(encpadding.as_raw_mut_slice()); + self.aes.clone().encrypt_block_mut(g); + let firstpadding: BitVec = BitVec::from_slice(g.as_slice()); + encrypted.set(0, firstpadding[0] ^ bits[0]); + + for n in 1..bits.len() { + padding.set(n - 1, bits[n - 1]); + + // encrypt padded - this is used as a psuedorandom function + let mut encpadding = padding.clone(); + let g = GenericArray::from_mut_slice(encpadding.as_raw_mut_slice()); + self.aes.clone().encrypt_block_mut(g); + let res: BitVec = BitVec::from_slice(g.as_slice()); + + // get the first bit of the encrypted value + encrypted.set(n, res[0] ^ bits[n]); + } + + encrypted + } +} diff --git a/cryptopan/tests/mod.rs b/cryptopan/tests/mod.rs new file mode 100644 index 000000000..131594333 --- /dev/null +++ b/cryptopan/tests/mod.rs @@ -0,0 +1,120 @@ +use aes::cipher::KeyInit; +use cryptopan::CryptoPAn; +use std::{net::Ipv4Addr, net::Ipv6Addr, str::FromStr}; + +const KEY: [u8; 32] = [ + 21, 34, 23, 141, 51, 164, 207, 128, 19, 10, 91, 22, 73, 144, 125, 16, 216, 152, 143, 131, 121, + 121, 101, 39, 98, 87, 76, 45, 42, 132, 34, 2, +]; + +#[test] +fn test_encrypt_ipv4() { + // This is a copy of the tests for the Go implementation + // https://github.com/Yawning/cryptopan/blob/master/cryptopan_test.go + // IPV4 tests + let ipv4 = [ + ("128.11.68.132", "135.242.180.132"), + ("129.118.74.4", "134.136.186.123"), + ("130.132.252.244", "133.68.164.234"), + ("141.223.7.43", "141.167.8.160"), + ("141.233.145.108", "141.129.237.235"), + ("152.163.225.39", "151.140.114.167"), + ("156.29.3.236", "147.225.12.42"), + ("165.247.96.84", "162.9.99.234"), + ("166.107.77.190", "160.132.178.185"), + ("192.102.249.13", "252.138.62.131"), + ("192.215.32.125", "252.43.47.189"), + ("192.233.80.103", "252.25.108.8"), + ("192.41.57.43", "252.222.221.184"), + ("193.150.244.223", "253.169.52.216"), + ("195.205.63.100", "255.186.223.5"), + ("198.200.171.101", "249.199.68.213"), + ("198.26.132.101", "249.36.123.202"), + ("198.36.213.5", "249.7.21.132"), + ("198.51.77.238", "249.18.186.254"), + ("199.217.79.101", "248.38.184.213"), + ("202.49.198.20", "245.206.7.234"), + ("203.12.160.252", "244.248.163.4"), + ("204.184.162.189", "243.192.77.90"), + ("204.202.136.230", "243.178.4.198"), + ("204.29.20.4", "243.33.20.123"), + ("205.178.38.67", "242.108.198.51"), + ("205.188.147.153", "242.96.16.101"), + ("205.188.248.25", "242.96.88.27"), + ("205.245.121.43", "242.21.121.163"), + ("207.105.49.5", "241.118.205.138"), + ("207.135.65.238", "241.202.129.222"), + ("207.155.9.214", "241.220.250.22"), + ("207.188.7.45", "241.255.249.220"), + ("207.25.71.27", "241.33.119.156"), + ("207.33.151.131", "241.1.233.131"), + ("208.147.89.59", "227.237.98.191"), + ("208.234.120.210", "227.154.67.17"), + ("208.28.185.184", "227.39.94.90"), + ("208.52.56.122", "227.8.63.165"), + ("209.12.231.7", "226.243.167.8"), + ("209.238.72.3", "226.6.119.243"), + ("209.246.74.109", "226.22.124.76"), + ("209.68.60.238", "226.184.220.233"), + ("209.85.249.6", "226.170.70.6"), + ("212.120.124.31", "228.135.163.231"), + ("212.146.8.236", "228.19.4.234"), + ("212.186.227.154", "228.59.98.98"), + ("212.204.172.118", "228.71.195.169"), + ("212.206.130.201", "228.69.242.193"), + ("216.148.237.145", "235.84.194.111"), + ("216.157.30.252", "235.89.31.26"), + ("216.184.159.48", "235.96.225.78"), + ("216.227.10.221", "235.28.253.36"), + ("216.254.18.172", "235.7.16.162"), + ("216.32.132.250", "235.192.139.38"), + ("216.35.217.178", "235.195.157.81"), + ("24.0.250.221", "100.15.198.226"), + ("24.13.62.231", "100.2.192.247"), + ("24.14.213.138", "100.1.42.141"), + ("24.5.0.80", "100.9.15.210"), + ("24.7.198.88", "100.10.6.25"), + ("24.94.26.44", "100.88.228.35"), + ("38.15.67.68", "64.3.66.187"), + ("4.3.88.225", "124.60.155.63"), + ("63.14.55.111", "95.9.215.7"), + ("63.195.241.44", "95.179.238.44"), + ("63.97.7.140", "95.97.9.123"), + ("64.14.118.196", "0.255.183.58"), + ("64.34.154.117", "0.221.154.117"), + ("64.39.15.238", "0.219.7.41"), + ]; + + for (k, v) in ipv4 { + assert_eq!( + v.to_string(), + CryptoPAn::new(&KEY.into()) + .encrypt_v4(Ipv4Addr::from_str(k).unwrap()) + .to_string() + ); + } +} + +#[test] +fn test_encrypt_ipv6() { + let ipv6 = [ + ( + "144:bc02:3f60:1dd9:7f02:8eff:f1e6:1edc", + "4479:8123:80c4:ac3d:728f:e372:ece:1c1e", + ), + ("::1", "ff78:1f0:c09f:df20:8083:f1b1:407:ed00"), + ("::2", "ff78:1f0:c09f:df20:8083:f1b1:407:ef00"), + ("::ffff", "ff78:1f0:c09f:df20:8083:f1b1:407:38f8"), + ("2001:db8::1", "144:bc02:3f60:1dd9:7f02:8eff:f1e6:1edc"), + ("2001:db8::2", "144:bc02:3f60:1dd9:7f02:8eff:f1e6:1cdc"), + ]; + + for (k, v) in ipv6 { + assert_eq!( + v.to_string(), + CryptoPAn::new(&KEY.into()) + .encrypt_v6(Ipv6Addr::from_str(k).unwrap()) + .to_string() + ); + } +}