use crate::random;
use crate::SodiumError;
pub const BYTES: usize = libsodium_sys::crypto_ipcrypt_BYTES as usize;
const IP_MAXLEN: usize = 46;
pub const KEYBYTES: usize = libsodium_sys::crypto_ipcrypt_KEYBYTES as usize;
#[derive(Clone, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
pub struct Key([u8; KEYBYTES]);
impl Key {
pub fn generate() -> Self {
let mut key = [0u8; KEYBYTES];
unsafe {
libsodium_sys::crypto_ipcrypt_keygen(key.as_mut_ptr());
}
Key(key)
}
pub fn from_slice(slice: &[u8]) -> Self {
assert_eq!(slice.len(), KEYBYTES, "key must be {KEYBYTES} bytes");
let mut key = [0u8; KEYBYTES];
key.copy_from_slice(slice);
Key(key)
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
impl AsRef<[u8]> for Key {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl From<[u8; KEYBYTES]> for Key {
fn from(bytes: [u8; KEYBYTES]) -> Self {
Key(bytes)
}
}
pub fn ipv4_to_bytes(addr: [u8; 4]) -> [u8; BYTES] {
let mut bytes = [0u8; BYTES];
bytes[10] = 0xff;
bytes[11] = 0xff;
bytes[12..].copy_from_slice(&addr);
bytes
}
pub fn bytes_to_ipv4(bytes: &[u8; BYTES]) -> Option<[u8; 4]> {
if bytes[..10].iter().all(|&b| b == 0) && bytes[10] == 0xff && bytes[11] == 0xff {
let mut addr = [0u8; 4];
addr.copy_from_slice(&bytes[12..]);
Some(addr)
} else {
None
}
}
pub fn parse_ip(ip: &str) -> Result<[u8; BYTES], SodiumError> {
let mut bin = [0u8; BYTES];
let ret = unsafe {
libsodium_sys::sodium_ip2bin(
bin.as_mut_ptr(),
ip.as_ptr() as *const std::os::raw::c_char,
ip.len(),
)
};
if ret == 0 {
Ok(bin)
} else {
Err(SodiumError::InvalidInput("invalid IP address".into()))
}
}
pub fn format_ip(bin: &[u8; BYTES]) -> String {
let mut buf = [0i8; IP_MAXLEN];
let ptr = unsafe {
libsodium_sys::sodium_bin2ip(
buf.as_mut_ptr() as *mut std::os::raw::c_char,
IP_MAXLEN,
bin.as_ptr(),
)
};
if ptr.is_null() {
String::new()
} else {
let len = buf.iter().position(|&c| c == 0).unwrap_or(IP_MAXLEN);
let bytes: Vec<u8> = buf[..len].iter().map(|&c| c as u8).collect();
String::from_utf8_lossy(&bytes).into_owned()
}
}
pub fn encrypt(input: &[u8; BYTES], key: &Key) -> [u8; BYTES] {
let mut output = [0u8; BYTES];
unsafe {
libsodium_sys::crypto_ipcrypt_encrypt(output.as_mut_ptr(), input.as_ptr(), key.0.as_ptr());
}
output
}
pub fn decrypt(input: &[u8; BYTES], key: &Key) -> [u8; BYTES] {
let mut output = [0u8; BYTES];
unsafe {
libsodium_sys::crypto_ipcrypt_decrypt(output.as_mut_ptr(), input.as_ptr(), key.0.as_ptr());
}
output
}
pub fn encrypt_str(ip: &str, key: &Key) -> Result<String, SodiumError> {
let bin = parse_ip(ip)?;
let encrypted = encrypt(&bin, key);
Ok(format_ip(&encrypted))
}
pub fn decrypt_str(ip: &str, key: &Key) -> Result<String, SodiumError> {
let bin = parse_ip(ip)?;
let decrypted = decrypt(&bin, key);
Ok(format_ip(&decrypted))
}
pub mod nd {
use super::*;
pub const KEYBYTES: usize = libsodium_sys::crypto_ipcrypt_ND_KEYBYTES as usize;
pub const TWEAKBYTES: usize = libsodium_sys::crypto_ipcrypt_ND_TWEAKBYTES as usize;
pub const INPUTBYTES: usize = libsodium_sys::crypto_ipcrypt_ND_INPUTBYTES as usize;
pub const OUTPUTBYTES: usize = libsodium_sys::crypto_ipcrypt_ND_OUTPUTBYTES as usize;
#[derive(Clone, Copy)]
pub struct Tweak([u8; TWEAKBYTES]);
impl Tweak {
pub fn random() -> Self {
let mut tweak = [0u8; TWEAKBYTES];
random::fill_bytes(&mut tweak);
Tweak(tweak)
}
pub fn from_slice(slice: &[u8]) -> Self {
assert_eq!(slice.len(), TWEAKBYTES, "tweak must be {TWEAKBYTES} bytes");
let mut tweak = [0u8; TWEAKBYTES];
tweak.copy_from_slice(slice);
Tweak(tweak)
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
impl From<[u8; TWEAKBYTES]> for Tweak {
fn from(bytes: [u8; TWEAKBYTES]) -> Self {
Tweak(bytes)
}
}
pub fn encrypt(input: &[u8; INPUTBYTES], tweak: &Tweak, key: &super::Key) -> [u8; OUTPUTBYTES] {
let mut output = [0u8; OUTPUTBYTES];
unsafe {
libsodium_sys::crypto_ipcrypt_nd_encrypt(
output.as_mut_ptr(),
input.as_ptr(),
tweak.0.as_ptr(),
key.0.as_ptr(),
);
}
output
}
pub fn decrypt(input: &[u8; OUTPUTBYTES], key: &super::Key) -> [u8; INPUTBYTES] {
let mut output = [0u8; INPUTBYTES];
unsafe {
libsodium_sys::crypto_ipcrypt_nd_decrypt(
output.as_mut_ptr(),
input.as_ptr(),
key.0.as_ptr(),
);
}
output
}
pub fn encrypt_str(ip: &str, key: &super::Key) -> Result<String, SodiumError> {
let bin = super::parse_ip(ip)?;
let tweak = Tweak::random();
let encrypted = encrypt(&bin, &tweak, key);
Ok(crate::utils::bin2hex(&encrypted))
}
pub fn decrypt_str(hex: &str, key: &super::Key) -> Result<String, SodiumError> {
let bytes = crate::utils::hex2bin(hex)?;
if bytes.len() != OUTPUTBYTES {
return Err(SodiumError::InvalidInput(format!(
"expected {} bytes, got {}",
OUTPUTBYTES,
bytes.len()
)));
}
let mut input = [0u8; OUTPUTBYTES];
input.copy_from_slice(&bytes);
let decrypted = decrypt(&input, key);
Ok(super::format_ip(&decrypted))
}
pub fn to_hex(ciphertext: &[u8; OUTPUTBYTES]) -> String {
crate::utils::bin2hex(ciphertext)
}
pub fn from_hex(hex: &str) -> Result<[u8; OUTPUTBYTES], SodiumError> {
let bytes = crate::utils::hex2bin(hex)?;
if bytes.len() != OUTPUTBYTES {
return Err(SodiumError::InvalidInput(format!(
"expected {} bytes, got {}",
OUTPUTBYTES,
bytes.len()
)));
}
let mut output = [0u8; OUTPUTBYTES];
output.copy_from_slice(&bytes);
Ok(output)
}
}
pub mod ndx {
use super::*;
pub const KEYBYTES: usize = libsodium_sys::crypto_ipcrypt_NDX_KEYBYTES as usize;
pub const TWEAKBYTES: usize = libsodium_sys::crypto_ipcrypt_NDX_TWEAKBYTES as usize;
pub const INPUTBYTES: usize = libsodium_sys::crypto_ipcrypt_NDX_INPUTBYTES as usize;
pub const OUTPUTBYTES: usize = libsodium_sys::crypto_ipcrypt_NDX_OUTPUTBYTES as usize;
#[derive(Clone, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
pub struct Key([u8; KEYBYTES]);
impl Key {
pub fn generate() -> Self {
let mut key = [0u8; KEYBYTES];
unsafe {
libsodium_sys::crypto_ipcrypt_ndx_keygen(key.as_mut_ptr());
}
Key(key)
}
pub fn from_slice(slice: &[u8]) -> Self {
assert_eq!(slice.len(), KEYBYTES, "key must be {KEYBYTES} bytes");
let mut key = [0u8; KEYBYTES];
key.copy_from_slice(slice);
Key(key)
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
impl AsRef<[u8]> for Key {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl From<[u8; KEYBYTES]> for Key {
fn from(bytes: [u8; KEYBYTES]) -> Self {
Key(bytes)
}
}
#[derive(Clone, Copy)]
pub struct Tweak([u8; TWEAKBYTES]);
impl Tweak {
pub fn random() -> Self {
let mut tweak = [0u8; TWEAKBYTES];
random::fill_bytes(&mut tweak);
Tweak(tweak)
}
pub fn from_slice(slice: &[u8]) -> Self {
assert_eq!(slice.len(), TWEAKBYTES, "tweak must be {TWEAKBYTES} bytes");
let mut tweak = [0u8; TWEAKBYTES];
tweak.copy_from_slice(slice);
Tweak(tweak)
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
impl From<[u8; TWEAKBYTES]> for Tweak {
fn from(bytes: [u8; TWEAKBYTES]) -> Self {
Tweak(bytes)
}
}
pub fn encrypt(input: &[u8; INPUTBYTES], tweak: &Tweak, key: &Key) -> [u8; OUTPUTBYTES] {
let mut output = [0u8; OUTPUTBYTES];
unsafe {
libsodium_sys::crypto_ipcrypt_ndx_encrypt(
output.as_mut_ptr(),
input.as_ptr(),
tweak.0.as_ptr(),
key.0.as_ptr(),
);
}
output
}
pub fn decrypt(input: &[u8; OUTPUTBYTES], key: &Key) -> [u8; INPUTBYTES] {
let mut output = [0u8; INPUTBYTES];
unsafe {
libsodium_sys::crypto_ipcrypt_ndx_decrypt(
output.as_mut_ptr(),
input.as_ptr(),
key.0.as_ptr(),
);
}
output
}
pub fn encrypt_str(ip: &str, key: &Key) -> Result<String, SodiumError> {
let bin = super::parse_ip(ip)?;
let tweak = Tweak::random();
let encrypted = encrypt(&bin, &tweak, key);
Ok(crate::utils::bin2hex(&encrypted))
}
pub fn decrypt_str(hex: &str, key: &Key) -> Result<String, SodiumError> {
let bytes = crate::utils::hex2bin(hex)?;
if bytes.len() != OUTPUTBYTES {
return Err(SodiumError::InvalidInput(format!(
"expected {} bytes, got {}",
OUTPUTBYTES,
bytes.len()
)));
}
let mut input = [0u8; OUTPUTBYTES];
input.copy_from_slice(&bytes);
let decrypted = decrypt(&input, key);
Ok(super::format_ip(&decrypted))
}
pub fn to_hex(ciphertext: &[u8; OUTPUTBYTES]) -> String {
crate::utils::bin2hex(ciphertext)
}
pub fn from_hex(hex: &str) -> Result<[u8; OUTPUTBYTES], SodiumError> {
let bytes = crate::utils::hex2bin(hex)?;
if bytes.len() != OUTPUTBYTES {
return Err(SodiumError::InvalidInput(format!(
"expected {} bytes, got {}",
OUTPUTBYTES,
bytes.len()
)));
}
let mut output = [0u8; OUTPUTBYTES];
output.copy_from_slice(&bytes);
Ok(output)
}
}
pub mod pfx {
pub const KEYBYTES: usize = libsodium_sys::crypto_ipcrypt_PFX_KEYBYTES as usize;
pub const BYTES: usize = libsodium_sys::crypto_ipcrypt_PFX_BYTES as usize;
#[derive(Clone, zeroize::Zeroize, zeroize::ZeroizeOnDrop)]
pub struct Key([u8; KEYBYTES]);
impl Key {
pub fn generate() -> Self {
let mut key = [0u8; KEYBYTES];
unsafe {
libsodium_sys::crypto_ipcrypt_pfx_keygen(key.as_mut_ptr());
}
Key(key)
}
pub fn from_slice(slice: &[u8]) -> Self {
assert_eq!(slice.len(), KEYBYTES, "key must be {KEYBYTES} bytes");
let mut key = [0u8; KEYBYTES];
key.copy_from_slice(slice);
Key(key)
}
pub fn as_bytes(&self) -> &[u8] {
&self.0
}
}
impl AsRef<[u8]> for Key {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl From<[u8; KEYBYTES]> for Key {
fn from(bytes: [u8; KEYBYTES]) -> Self {
Key(bytes)
}
}
pub fn encrypt(input: &[u8; BYTES], key: &Key) -> [u8; BYTES] {
let mut output = [0u8; BYTES];
unsafe {
libsodium_sys::crypto_ipcrypt_pfx_encrypt(
output.as_mut_ptr(),
input.as_ptr(),
key.0.as_ptr(),
);
}
output
}
pub fn decrypt(input: &[u8; BYTES], key: &Key) -> [u8; BYTES] {
let mut output = [0u8; BYTES];
unsafe {
libsodium_sys::crypto_ipcrypt_pfx_decrypt(
output.as_mut_ptr(),
input.as_ptr(),
key.0.as_ptr(),
);
}
output
}
pub fn encrypt_str(ip: &str, key: &Key) -> Result<String, super::SodiumError> {
let bin = super::parse_ip(ip)?;
let encrypted = encrypt(&bin, key);
Ok(super::format_ip(&encrypted))
}
pub fn decrypt_str(ip: &str, key: &Key) -> Result<String, super::SodiumError> {
let bin = super::parse_ip(ip)?;
let decrypted = decrypt(&bin, key);
Ok(super::format_ip(&decrypted))
}
}
#[cfg(test)]
mod tests {
use super::{
bytes_to_ipv4, decrypt, encrypt, ipv4_to_bytes, nd, ndx, pfx, Key, BYTES, KEYBYTES,
};
#[test]
fn test_ipv4_conversion() {
let ipv4 = [192, 0, 2, 1];
let bytes = ipv4_to_bytes(ipv4);
assert!(bytes[..10].iter().all(|&b| b == 0));
assert_eq!(bytes[10], 0xff);
assert_eq!(bytes[11], 0xff);
assert_eq!(&bytes[12..], &ipv4);
let recovered = bytes_to_ipv4(&bytes).unwrap();
assert_eq!(recovered, ipv4);
}
#[test]
fn test_deterministic_encryption() {
let key = Key::generate();
let ip = ipv4_to_bytes([192, 0, 2, 1]);
let encrypted = encrypt(&ip, &key);
let decrypted = decrypt(&encrypted, &key);
assert_eq!(ip, decrypted);
assert_ne!(ip, encrypted);
}
#[test]
fn test_deterministic_same_input_same_output() {
let key = Key::generate();
let ip = ipv4_to_bytes([10, 0, 0, 1]);
let encrypted1 = encrypt(&ip, &key);
let encrypted2 = encrypt(&ip, &key);
assert_eq!(encrypted1, encrypted2);
}
#[test]
fn test_nd_encryption() {
let key = Key::generate();
let ip = ipv4_to_bytes([192, 168, 1, 1]);
let tweak = nd::Tweak::random();
let encrypted = nd::encrypt(&ip, &tweak, &key);
let decrypted = nd::decrypt(&encrypted, &key);
assert_eq!(ip, decrypted);
assert_eq!(encrypted.len(), nd::OUTPUTBYTES);
}
#[test]
fn test_nd_different_tweaks() {
let key = Key::generate();
let ip = ipv4_to_bytes([192, 168, 1, 1]);
let tweak1 = nd::Tweak::random();
let tweak2 = nd::Tweak::random();
let encrypted1 = nd::encrypt(&ip, &tweak1, &key);
let encrypted2 = nd::encrypt(&ip, &tweak2, &key);
assert_ne!(encrypted1, encrypted2);
let decrypted1 = nd::decrypt(&encrypted1, &key);
let decrypted2 = nd::decrypt(&encrypted2, &key);
assert_eq!(decrypted1, ip);
assert_eq!(decrypted2, ip);
}
#[test]
fn test_ndx_encryption() {
let key = ndx::Key::generate();
let ip = ipv4_to_bytes([172, 16, 0, 1]);
let tweak = ndx::Tweak::random();
let encrypted = ndx::encrypt(&ip, &tweak, &key);
let decrypted = ndx::decrypt(&encrypted, &key);
assert_eq!(ip, decrypted);
assert_eq!(encrypted.len(), ndx::OUTPUTBYTES);
}
#[test]
fn test_ndx_different_tweaks() {
let key = ndx::Key::generate();
let ip = ipv4_to_bytes([172, 16, 0, 1]);
let tweak1 = ndx::Tweak::random();
let tweak2 = ndx::Tweak::random();
let encrypted1 = ndx::encrypt(&ip, &tweak1, &key);
let encrypted2 = ndx::encrypt(&ip, &tweak2, &key);
assert_ne!(encrypted1, encrypted2);
assert_eq!(ndx::decrypt(&encrypted1, &key), ip);
assert_eq!(ndx::decrypt(&encrypted2, &key), ip);
}
#[test]
fn test_pfx_encryption() {
let key = pfx::Key::generate();
let ip = ipv4_to_bytes([10, 0, 0, 1]);
let encrypted = pfx::encrypt(&ip, &key);
let decrypted = pfx::decrypt(&encrypted, &key);
assert_eq!(ip, decrypted);
assert_eq!(encrypted.len(), pfx::BYTES);
}
#[test]
fn test_pfx_preserves_relationship() {
let key = pfx::Key::generate();
let ip1 = ipv4_to_bytes([10, 0, 0, 1]);
let ip2 = ipv4_to_bytes([10, 0, 0, 100]);
let enc1 = pfx::encrypt(&ip1, &key);
let enc2 = pfx::encrypt(&ip2, &key);
assert_eq!(pfx::decrypt(&enc1, &key), ip1);
assert_eq!(pfx::decrypt(&enc2, &key), ip2);
assert_ne!(enc1, enc2);
}
#[test]
fn test_different_keys_different_results() {
let key1 = Key::generate();
let key2 = Key::generate();
let ip = ipv4_to_bytes([192, 0, 2, 1]);
let encrypted1 = encrypt(&ip, &key1);
let encrypted2 = encrypt(&ip, &key2);
assert_ne!(encrypted1, encrypted2);
}
#[test]
fn test_key_from_slice() {
let bytes = [0x42u8; KEYBYTES];
let key = Key::from_slice(&bytes);
assert_eq!(key.as_bytes(), &bytes);
}
#[test]
fn test_ndx_key_from_slice() {
let bytes = [0x42u8; ndx::KEYBYTES];
let key = ndx::Key::from_slice(&bytes);
assert_eq!(key.as_bytes(), &bytes);
}
#[test]
fn test_pfx_key_from_slice() {
let bytes = [0x42u8; pfx::KEYBYTES];
let key = pfx::Key::from_slice(&bytes);
assert_eq!(key.as_bytes(), &bytes);
}
#[test]
fn test_ipv6_encryption() {
let key = Key::generate();
let ip: [u8; BYTES] = [
0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x01,
];
let encrypted = encrypt(&ip, &key);
let decrypted = decrypt(&encrypted, &key);
assert_eq!(ip, decrypted);
assert!(bytes_to_ipv4(&ip).is_none());
}
#[test]
fn test_constants() {
assert_eq!(BYTES, 16);
assert_eq!(KEYBYTES, 16);
assert_eq!(nd::KEYBYTES, 16);
assert_eq!(nd::TWEAKBYTES, 8);
assert_eq!(nd::INPUTBYTES, 16);
assert_eq!(nd::OUTPUTBYTES, 24);
assert_eq!(ndx::KEYBYTES, 32);
assert_eq!(ndx::TWEAKBYTES, 16);
assert_eq!(ndx::INPUTBYTES, 16);
assert_eq!(ndx::OUTPUTBYTES, 32);
assert_eq!(pfx::KEYBYTES, 32);
assert_eq!(pfx::BYTES, 16);
}
#[test]
fn test_parse_ip_ipv4() {
use super::{format_ip, parse_ip};
let bin = parse_ip("192.0.2.1").unwrap();
assert_eq!(format_ip(&bin), "192.0.2.1");
let bin = parse_ip("10.0.0.1").unwrap();
assert_eq!(format_ip(&bin), "10.0.0.1");
let bin = parse_ip("255.255.255.255").unwrap();
assert_eq!(format_ip(&bin), "255.255.255.255");
}
#[test]
fn test_parse_ip_ipv6() {
use super::{format_ip, parse_ip};
let bin = parse_ip("2001:db8::1").unwrap();
assert_eq!(format_ip(&bin), "2001:db8::1");
let bin = parse_ip("::1").unwrap();
assert_eq!(format_ip(&bin), "::1");
let bin = parse_ip("fe80::1").unwrap();
assert_eq!(format_ip(&bin), "fe80::1");
let bin = parse_ip("2001:0db8:85a3:0000:0000:8a2e:0370:7334").unwrap();
let formatted = format_ip(&bin);
assert!(formatted.contains("2001:"));
}
#[test]
fn test_parse_ip_invalid() {
use super::parse_ip;
assert!(parse_ip("invalid").is_err());
assert!(parse_ip("256.0.0.1").is_err());
assert!(parse_ip("").is_err());
}
#[test]
fn test_encrypt_str_deterministic() {
use super::{decrypt_str, encrypt_str, Key};
let key = Key::generate();
let encrypted = encrypt_str("192.0.2.1", &key).unwrap();
let decrypted = decrypt_str(&encrypted, &key).unwrap();
assert_eq!(decrypted, "192.0.2.1");
let encrypted = encrypt_str("2001:db8::1", &key).unwrap();
let decrypted = decrypt_str(&encrypted, &key).unwrap();
assert_eq!(decrypted, "2001:db8::1");
}
#[test]
fn test_nd_encrypt_str() {
let key = Key::generate();
let encrypted = nd::encrypt_str("192.0.2.1", &key).unwrap();
assert_eq!(encrypted.len(), nd::OUTPUTBYTES * 2);
let decrypted = nd::decrypt_str(&encrypted, &key).unwrap();
assert_eq!(decrypted, "192.0.2.1");
let encrypted = nd::encrypt_str("2001:db8::1", &key).unwrap();
let decrypted = nd::decrypt_str(&encrypted, &key).unwrap();
assert_eq!(decrypted, "2001:db8::1");
}
#[test]
fn test_nd_hex_encoding() {
let key = Key::generate();
let ip = ipv4_to_bytes([192, 0, 2, 1]);
let tweak = nd::Tweak::random();
let encrypted = nd::encrypt(&ip, &tweak, &key);
let hex = nd::to_hex(&encrypted);
assert_eq!(hex.len(), nd::OUTPUTBYTES * 2);
let decoded = nd::from_hex(&hex).unwrap();
assert_eq!(encrypted, decoded);
}
#[test]
fn test_ndx_encrypt_str() {
let key = ndx::Key::generate();
let encrypted = ndx::encrypt_str("192.0.2.1", &key).unwrap();
assert_eq!(encrypted.len(), ndx::OUTPUTBYTES * 2);
let decrypted = ndx::decrypt_str(&encrypted, &key).unwrap();
assert_eq!(decrypted, "192.0.2.1");
let encrypted = ndx::encrypt_str("2001:db8::1", &key).unwrap();
let decrypted = ndx::decrypt_str(&encrypted, &key).unwrap();
assert_eq!(decrypted, "2001:db8::1");
}
#[test]
fn test_ndx_hex_encoding() {
let key = ndx::Key::generate();
let ip = ipv4_to_bytes([192, 0, 2, 1]);
let tweak = ndx::Tweak::random();
let encrypted = ndx::encrypt(&ip, &tweak, &key);
let hex = ndx::to_hex(&encrypted);
assert_eq!(hex.len(), ndx::OUTPUTBYTES * 2);
let decoded = ndx::from_hex(&hex).unwrap();
assert_eq!(encrypted, decoded);
}
#[test]
fn test_pfx_encrypt_str() {
let key = pfx::Key::generate();
let encrypted = pfx::encrypt_str("192.0.2.1", &key).unwrap();
let decrypted = pfx::decrypt_str(&encrypted, &key).unwrap();
assert_eq!(decrypted, "192.0.2.1");
let encrypted = pfx::encrypt_str("2001:db8::1", &key).unwrap();
let decrypted = pfx::decrypt_str(&encrypted, &key).unwrap();
assert_eq!(decrypted, "2001:db8::1");
}
#[test]
fn test_format_ip_consistency_with_manual_conversion() {
use super::{bytes_to_ipv4, format_ip, ipv4_to_bytes, parse_ip};
let manual = ipv4_to_bytes([192, 0, 2, 1]);
let parsed = parse_ip("192.0.2.1").unwrap();
assert_eq!(manual, parsed);
let formatted = format_ip(&manual);
assert_eq!(formatted, "192.0.2.1");
let ipv4 = bytes_to_ipv4(&parsed).unwrap();
assert_eq!(ipv4, [192, 0, 2, 1]);
}
}