diff --git a/src/libsodium/crypto_ipcrypt/crypto_ipcrypt.c b/src/libsodium/crypto_ipcrypt/crypto_ipcrypt.c index 6c889043..aceb06c1 100644 --- a/src/libsodium/crypto_ipcrypt/crypto_ipcrypt.c +++ b/src/libsodium/crypto_ipcrypt/crypto_ipcrypt.c @@ -80,6 +80,18 @@ crypto_ipcrypt_ndx_outputbytes(void) return crypto_ipcrypt_NDX_OUTPUTBYTES; } +size_t +crypto_ipcrypt_pfx_keybytes(void) +{ + return crypto_ipcrypt_PFX_KEYBYTES; +} + +size_t +crypto_ipcrypt_pfx_bytes(void) +{ + return crypto_ipcrypt_PFX_BYTES; +} + void crypto_ipcrypt_keygen(unsigned char k[crypto_ipcrypt_KEYBYTES]) { @@ -92,6 +104,12 @@ crypto_ipcrypt_ndx_keygen(unsigned char k[crypto_ipcrypt_NDX_KEYBYTES]) randombytes_buf(k, crypto_ipcrypt_NDX_KEYBYTES); } +void +crypto_ipcrypt_pfx_keygen(unsigned char k[crypto_ipcrypt_PFX_KEYBYTES]) +{ + randombytes_buf(k, crypto_ipcrypt_PFX_KEYBYTES); +} + void crypto_ipcrypt_encrypt(unsigned char *out, const unsigned char *in, const unsigned char *k) { @@ -134,6 +152,18 @@ crypto_ipcrypt_ndx_decrypt(unsigned char *out, const unsigned char *in, const un implementation->ndx_decrypt(out, in, k); } +void +crypto_ipcrypt_pfx_encrypt(unsigned char *out, const unsigned char *in, const unsigned char *k) +{ + implementation->pfx_encrypt(out, in, k); +} + +void +crypto_ipcrypt_pfx_decrypt(unsigned char *out, const unsigned char *in, const unsigned char *k) +{ + implementation->pfx_decrypt(out, in, k); +} + int _crypto_ipcrypt_pick_best_implementation(void) { diff --git a/src/libsodium/crypto_ipcrypt/implementations.h b/src/libsodium/crypto_ipcrypt/implementations.h index 42ab5824..c4b5ab60 100644 --- a/src/libsodium/crypto_ipcrypt/implementations.h +++ b/src/libsodium/crypto_ipcrypt/implementations.h @@ -13,6 +13,8 @@ typedef struct ipcrypt_implementation { void (*nd_decrypt)(uint8_t *out, const uint8_t *in, const uint8_t *k); void (*ndx_encrypt)(uint8_t *out, const uint8_t *in, const uint8_t *t, const uint8_t *k); void (*ndx_decrypt)(uint8_t *out, const uint8_t *in, const uint8_t *k); + void (*pfx_encrypt)(uint8_t *out, const uint8_t *in, const uint8_t *k); + void (*pfx_decrypt)(uint8_t *out, const uint8_t *in, const uint8_t *k); } ipcrypt_implementation; #endif diff --git a/src/libsodium/crypto_ipcrypt/ipcrypt_aesni.c b/src/libsodium/crypto_ipcrypt/ipcrypt_aesni.c index f1c24d4d..803ef32f 100644 --- a/src/libsodium/crypto_ipcrypt/ipcrypt_aesni.c +++ b/src/libsodium/crypto_ipcrypt/ipcrypt_aesni.c @@ -286,10 +286,204 @@ ndx_decrypt(uint8_t *out, const uint8_t *in, const uint8_t *k) aes_xex_decrypt(out, in + 16, in, tkeys, rkeys); } +static int +is_ipv4_mapped(const uint8_t ip16[16]) +{ + static const uint8_t ipv4_mapped_prefix[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; + + return memcmp(ip16, ipv4_mapped_prefix, 12) == 0; +} + +static uint8_t +pfx_get_bit(const uint8_t ip16[16], unsigned int bit_index) +{ + return (ip16[15 - bit_index / 8] >> (bit_index % 8)) & 1; +} + +static void +pfx_set_bit(uint8_t ip16[16], const unsigned int bit_index, const uint8_t bit_value) +{ + const size_t byte_index = 15 - bit_index / 8; + const uint8_t bit_mask = (uint8_t) (1 << (bit_index % 8)); + uint8_t mask = (uint8_t) -((bit_value & 1)); + +#if defined(__GNUC__) || defined(__clang__) + __asm__ __volatile__("" : "+r"(mask) ::); +#endif + ip16[byte_index] = (ip16[byte_index] & ~bit_mask) | (bit_mask & mask); +} + +static void +pfx_shift_left(uint8_t ip16[16]) +{ + BlockVec v = LOAD128(ip16); + BlockVec shl = _mm_slli_epi64(v, 1); + BlockVec shr = _mm_srli_epi64(v, 63); + BlockVec car = _mm_slli_si128(shr, 8); + v = _mm_or_si128(shl, car); + STORE128(ip16, v); +} + +static void +pfx_pad_prefix(uint8_t padded_prefix[16], unsigned int prefix_len_bits) +{ + memset(padded_prefix, 0, 16); + if (prefix_len_bits == 0) { + padded_prefix[15] = 0x01; + } else { + padded_prefix[3] = 0x01; + padded_prefix[14] = 0xff; + padded_prefix[15] = 0xff; + } +} + +static void +pfx_encrypt(uint8_t *out, const uint8_t *in, const uint8_t *k) +{ + KeySchedule k1keys; + KeySchedule k2keys; + uint8_t diff[16]; + uint8_t encrypted[16]; + uint8_t padded_prefix[16]; + uint8_t t[16]; + BlockVec e1, e2, e; + unsigned int prefix_start = 0; + unsigned int prefix_len_bits; + unsigned int bit_pos; + uint8_t cipher_bit; + uint8_t original_bit; + size_t i; + uint8_t d; + + expand_key(k1keys, k); + expand_key(k2keys, k + 16); + + STORE128(diff, XOR128(k1keys[ROUNDS / 2], k2keys[ROUNDS / 2])); + d = 0; + for (i = 0; i < 16; i++) { + d |= diff[i]; + } + if (d == 0) { + for (i = 0; i < 16; i++) { + diff[i] = k[i] ^ 0x5a; + } + expand_key(k2keys, diff); + } + + if (is_ipv4_mapped(in)) { + prefix_start = 96; + } + + pfx_pad_prefix(padded_prefix, prefix_start); + + memset(encrypted, 0, 16); + if (prefix_start == 96) { + encrypted[10] = 0xff; + encrypted[11] = 0xff; + } + + for (prefix_len_bits = prefix_start; prefix_len_bits < 128; prefix_len_bits++) { + e1 = XOR128(LOAD128(padded_prefix), k1keys[0]); + e2 = XOR128(LOAD128(padded_prefix), k2keys[0]); + for (i = 1; i < ROUNDS; i++) { + e1 = AES_ENCRYPT(e1, k1keys[i]); + e2 = AES_ENCRYPT(e2, k2keys[i]); + } + e1 = AES_ENCRYPTLAST(e1, k1keys[ROUNDS]); + e2 = AES_ENCRYPTLAST(e2, k2keys[ROUNDS]); + + e = XOR128(e1, e2); + STORE128(t, e); + + cipher_bit = t[15] & 1; + bit_pos = 127 - prefix_len_bits; + original_bit = pfx_get_bit(in, bit_pos); + pfx_set_bit(encrypted, bit_pos, original_bit ^ cipher_bit); + + pfx_shift_left(padded_prefix); + pfx_set_bit(padded_prefix, 0, original_bit); + } + + memcpy(out, encrypted, 16); +} + +static void +pfx_decrypt(uint8_t *out, const uint8_t *in, const uint8_t *k) +{ + KeySchedule k1keys; + KeySchedule k2keys; + uint8_t diff[16]; + uint8_t decrypted[16]; + uint8_t padded_prefix[16]; + uint8_t t[16]; + BlockVec e1, e2, e; + unsigned int prefix_start = 0; + unsigned int prefix_len_bits; + unsigned int bit_pos; + uint8_t cipher_bit; + uint8_t encrypted_bit; + uint8_t original_bit; + size_t i; + uint8_t d; + + expand_key(k1keys, k); + expand_key(k2keys, k + 16); + + STORE128(diff, XOR128(k1keys[ROUNDS / 2], k2keys[ROUNDS / 2])); + d = 0; + for (i = 0; i < 16; i++) { + d |= diff[i]; + } + if (d == 0) { + for (i = 0; i < 16; i++) { + diff[i] = k[i] ^ 0x5a; + } + expand_key(k2keys, diff); + } + + if (is_ipv4_mapped(in)) { + prefix_start = 96; + } + + pfx_pad_prefix(padded_prefix, prefix_start); + + memset(decrypted, 0, 16); + if (prefix_start == 96) { + decrypted[10] = 0xff; + decrypted[11] = 0xff; + } + + for (prefix_len_bits = prefix_start; prefix_len_bits < 128; prefix_len_bits++) { + e1 = XOR128(LOAD128(padded_prefix), k1keys[0]); + e2 = XOR128(LOAD128(padded_prefix), k2keys[0]); + for (i = 1; i < ROUNDS; i++) { + e1 = AES_ENCRYPT(e1, k1keys[i]); + e2 = AES_ENCRYPT(e2, k2keys[i]); + } + e1 = AES_ENCRYPTLAST(e1, k1keys[ROUNDS]); + e2 = AES_ENCRYPTLAST(e2, k2keys[ROUNDS]); + + e = XOR128(e1, e2); + STORE128(t, e); + + cipher_bit = t[15] & 1; + bit_pos = 127 - prefix_len_bits; + encrypted_bit = pfx_get_bit(in, bit_pos); + original_bit = encrypted_bit ^ cipher_bit; + pfx_set_bit(decrypted, bit_pos, original_bit); + + pfx_shift_left(padded_prefix); + pfx_set_bit(padded_prefix, 0, original_bit); + } + + memcpy(out, decrypted, 16); +} + struct ipcrypt_implementation ipcrypt_aesni_implementation = { SODIUM_C99(.encrypt =) encrypt, SODIUM_C99(.decrypt =) decrypt, SODIUM_C99(.nd_encrypt =) nd_encrypt, SODIUM_C99(.nd_decrypt =) nd_decrypt, - SODIUM_C99(.ndx_encrypt =) ndx_encrypt, SODIUM_C99(.ndx_decrypt =) ndx_decrypt + SODIUM_C99(.ndx_encrypt =) ndx_encrypt, SODIUM_C99(.ndx_decrypt =) ndx_decrypt, + SODIUM_C99(.pfx_encrypt =) pfx_encrypt, SODIUM_C99(.pfx_decrypt =) pfx_decrypt }; # ifdef __clang__ diff --git a/src/libsodium/crypto_ipcrypt/ipcrypt_armcrypto.c b/src/libsodium/crypto_ipcrypt/ipcrypt_armcrypto.c index 12481c74..26bfe0bf 100644 --- a/src/libsodium/crypto_ipcrypt/ipcrypt_armcrypto.c +++ b/src/libsodium/crypto_ipcrypt/ipcrypt_armcrypto.c @@ -318,10 +318,209 @@ ndx_decrypt(uint8_t *out, const uint8_t *in, const uint8_t *k) aes_xex_decrypt(out, in + 16, in, tkeys, rkeys); } +static int +is_ipv4_mapped(const uint8_t ip16[16]) +{ + static const uint8_t ipv4_mapped_prefix[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; + + return memcmp(ip16, ipv4_mapped_prefix, 12) == 0; +} + +static uint8_t +pfx_get_bit(const uint8_t ip16[16], unsigned int bit_index) +{ + return (ip16[15 - bit_index / 8] >> (bit_index % 8)) & 1; +} + +static void +pfx_set_bit(uint8_t ip16[16], const unsigned int bit_index, const uint8_t bit_value) +{ + const size_t byte_index = 15 - bit_index / 8; + const uint8_t bit_mask = (uint8_t) (1 << (bit_index % 8)); + uint8_t mask = (uint8_t) -((bit_value & 1)); + +#if defined(__GNUC__) || defined(__clang__) + __asm__ __volatile__("" : "+r"(mask) ::); +#endif + ip16[byte_index] = (ip16[byte_index] & ~bit_mask) | (bit_mask & mask); +} + +static void +pfx_shift_left(uint8_t ip16[16]) +{ + BlockVec v = LOAD128(ip16); + const BlockVec shl = vshlq_n_u8(vreinterpretq_u8_u64(v), 1); + const BlockVec msb = vshrq_n_u8(vreinterpretq_u8_u64(v), 7); + const BlockVec zero = vdupq_n_u8(0); + const BlockVec carries = vextq_u8(vreinterpretq_u8_u64(msb), zero, 1); + v = vreinterpretq_u64_u8(vorrq_u8(shl, carries)); + STORE128(ip16, v); +} + +static void +pfx_pad_prefix(uint8_t padded_prefix[16], unsigned int prefix_len_bits) +{ + memset(padded_prefix, 0, 16); + if (prefix_len_bits == 0) { + padded_prefix[15] = 0x01; + } else { + padded_prefix[3] = 0x01; + padded_prefix[14] = 0xff; + padded_prefix[15] = 0xff; + } +} + +static void +pfx_encrypt(uint8_t *out, const uint8_t *in, const uint8_t *k) +{ + KeySchedule k1keys; + KeySchedule k2keys; + uint8_t diff[16]; + uint8_t encrypted[16]; + uint8_t padded_prefix[16]; + uint8_t t[16]; + BlockVec e1, e2, e; + unsigned int prefix_start = 0; + unsigned int prefix_len_bits; + unsigned int bit_pos; + uint8_t cipher_bit; + uint8_t original_bit; + size_t i; + uint8_t d; + + expand_key(k1keys, k); + expand_key(k2keys, k + 16); + + STORE128(diff, XOR128(k1keys[ROUNDS / 2], k2keys[ROUNDS / 2])); + d = 0; + for (i = 0; i < 16; i++) { + d |= diff[i]; + } + if (d == 0) { + for (i = 0; i < 16; i++) { + diff[i] = k[i] ^ 0x5a; + } + expand_key(k2keys, diff); + } + + if (is_ipv4_mapped(in)) { + prefix_start = 96; + } + + pfx_pad_prefix(padded_prefix, prefix_start); + + memset(encrypted, 0, 16); + if (prefix_start == 96) { + encrypted[10] = 0xff; + encrypted[11] = 0xff; + } + + for (prefix_len_bits = prefix_start; prefix_len_bits < 128; prefix_len_bits++) { + e1 = AES_XENCRYPT(LOAD128(padded_prefix), k1keys[0]); + e2 = AES_XENCRYPT(LOAD128(padded_prefix), k2keys[0]); + for (i = 1; i < ROUNDS - 1; i++) { + e1 = AES_XENCRYPT(e1, k1keys[i]); + e2 = AES_XENCRYPT(e2, k2keys[i]); + } + e1 = AES_XENCRYPTLAST(e1, k1keys[i]); + e2 = AES_XENCRYPTLAST(e2, k2keys[i]); + e1 = XOR128(e1, k1keys[ROUNDS]); + e2 = XOR128(e2, k2keys[ROUNDS]); + + e = XOR128(e1, e2); + STORE128(t, e); + + cipher_bit = t[15] & 1; + bit_pos = 127 - prefix_len_bits; + original_bit = pfx_get_bit(in, bit_pos); + pfx_set_bit(encrypted, bit_pos, original_bit ^ cipher_bit); + + pfx_shift_left(padded_prefix); + pfx_set_bit(padded_prefix, 0, original_bit); + } + + memcpy(out, encrypted, 16); +} + +static void +pfx_decrypt(uint8_t *out, const uint8_t *in, const uint8_t *k) +{ + KeySchedule k1keys; + KeySchedule k2keys; + uint8_t diff[16]; + uint8_t decrypted[16]; + uint8_t padded_prefix[16]; + uint8_t t[16]; + BlockVec e1, e2, e; + unsigned int prefix_start = 0; + unsigned int prefix_len_bits; + unsigned int bit_pos; + uint8_t cipher_bit; + uint8_t encrypted_bit; + uint8_t original_bit; + size_t i; + uint8_t d; + + expand_key(k1keys, k); + expand_key(k2keys, k + 16); + + STORE128(diff, XOR128(k1keys[ROUNDS / 2], k2keys[ROUNDS / 2])); + d = 0; + for (i = 0; i < 16; i++) { + d |= diff[i]; + } + if (d == 0) { + for (i = 0; i < 16; i++) { + diff[i] = k[i] ^ 0x5a; + } + expand_key(k2keys, diff); + } + + if (is_ipv4_mapped(in)) { + prefix_start = 96; + } + + pfx_pad_prefix(padded_prefix, prefix_start); + + memset(decrypted, 0, 16); + if (prefix_start == 96) { + decrypted[10] = 0xff; + decrypted[11] = 0xff; + } + + for (prefix_len_bits = prefix_start; prefix_len_bits < 128; prefix_len_bits++) { + e1 = AES_XENCRYPT(LOAD128(padded_prefix), k1keys[0]); + e2 = AES_XENCRYPT(LOAD128(padded_prefix), k2keys[0]); + for (i = 1; i < ROUNDS - 1; i++) { + e1 = AES_XENCRYPT(e1, k1keys[i]); + e2 = AES_XENCRYPT(e2, k2keys[i]); + } + e1 = AES_XENCRYPTLAST(e1, k1keys[i]); + e2 = AES_XENCRYPTLAST(e2, k2keys[i]); + e1 = XOR128(e1, k1keys[ROUNDS]); + e2 = XOR128(e2, k2keys[ROUNDS]); + + e = XOR128(e1, e2); + STORE128(t, e); + + cipher_bit = t[15] & 1; + bit_pos = 127 - prefix_len_bits; + encrypted_bit = pfx_get_bit(in, bit_pos); + original_bit = encrypted_bit ^ cipher_bit; + pfx_set_bit(decrypted, bit_pos, original_bit); + + pfx_shift_left(padded_prefix); + pfx_set_bit(padded_prefix, 0, original_bit); + } + + memcpy(out, decrypted, 16); +} + struct ipcrypt_implementation ipcrypt_armcrypto_implementation = { SODIUM_C99(.encrypt =) encrypt, SODIUM_C99(.decrypt =) decrypt, SODIUM_C99(.nd_encrypt =) nd_encrypt, SODIUM_C99(.nd_decrypt =) nd_decrypt, - SODIUM_C99(.ndx_encrypt =) ndx_encrypt, SODIUM_C99(.ndx_decrypt =) ndx_decrypt + SODIUM_C99(.ndx_encrypt =) ndx_encrypt, SODIUM_C99(.ndx_decrypt =) ndx_decrypt, + SODIUM_C99(.pfx_encrypt =) pfx_encrypt, SODIUM_C99(.pfx_decrypt =) pfx_decrypt }; # ifdef __clang__ diff --git a/src/libsodium/crypto_ipcrypt/ipcrypt_soft.c b/src/libsodium/crypto_ipcrypt/ipcrypt_soft.c index d243434f..4be57e23 100644 --- a/src/libsodium/crypto_ipcrypt/ipcrypt_soft.c +++ b/src/libsodium/crypto_ipcrypt/ipcrypt_soft.c @@ -260,8 +260,202 @@ ndx_decrypt(uint8_t *out, const uint8_t *in, const uint8_t *k) aes_xex_decrypt(out, in + 16, in, tkeys, rkeys); } +static int +is_ipv4_mapped(const uint8_t ip16[16]) +{ + static const uint8_t ipv4_mapped_prefix[12] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0xff }; + + return memcmp(ip16, ipv4_mapped_prefix, 12) == 0; +} + +static uint8_t +pfx_get_bit(const uint8_t ip16[16], unsigned int bit_index) +{ + return (ip16[15 - bit_index / 8] >> (bit_index % 8)) & 1; +} + +static void +pfx_set_bit(uint8_t ip16[16], const unsigned int bit_index, const uint8_t bit_value) +{ + const size_t byte_index = 15 - bit_index / 8; + const uint8_t bit_mask = (uint8_t) (1 << (bit_index % 8)); + uint8_t mask = (uint8_t) -((bit_value & 1)); + +#if defined(__GNUC__) || defined(__clang__) + __asm__ __volatile__("" : "+r"(mask) ::); +#endif + ip16[byte_index] = (ip16[byte_index] & ~bit_mask) | (bit_mask & mask); +} + +static void +pfx_shift_left(uint8_t ip16[16]) +{ + size_t i; + + for (i = 0; i < 15; i++) { + ip16[i] = (ip16[i] << 1) | (ip16[i + 1] >> 7); + } + ip16[15] <<= 1; +} + +static void +pfx_pad_prefix(uint8_t padded_prefix[16], unsigned int prefix_len_bits) +{ + memset(padded_prefix, 0, 16); + if (prefix_len_bits == 0) { + padded_prefix[15] = 0x01; + } else { + padded_prefix[3] = 0x01; + padded_prefix[14] = 0xff; + padded_prefix[15] = 0xff; + } +} + +static void +pfx_encrypt(uint8_t *out, const uint8_t *in, const uint8_t *k) +{ + KeySchedule k1keys; + KeySchedule k2keys; + uint8_t diff[16]; + uint8_t encrypted[16]; + uint8_t padded_prefix[16]; + uint8_t t[16]; + aes_block_t e1, e2, e; + unsigned int prefix_start = 0; + unsigned int prefix_len_bits; + unsigned int bit_pos; + uint8_t cipher_bit; + uint8_t original_bit; + size_t i; + uint8_t d; + + expand_key(k1keys, k); + expand_key(k2keys, k + 16); + + AES_BLOCK_STORE(diff, AES_BLOCK_XOR(k1keys[ROUNDS / 2], k2keys[ROUNDS / 2])); + d = 0; + for (i = 0; i < 16; i++) { + d |= diff[i]; + } + if (d == 0) { + for (i = 0; i < 16; i++) { + diff[i] = k[i] ^ 0x5a; + } + expand_key(k2keys, diff); + } + + if (is_ipv4_mapped(in)) { + prefix_start = 96; + } + + pfx_pad_prefix(padded_prefix, prefix_start); + + memset(encrypted, 0, 16); + if (prefix_start == 96) { + encrypted[10] = 0xff; + encrypted[11] = 0xff; + } + + for (prefix_len_bits = prefix_start; prefix_len_bits < 128; prefix_len_bits++) { + e1 = AES_BLOCK_XOR(AES_BLOCK_LOAD(padded_prefix), k1keys[0]); + e2 = AES_BLOCK_XOR(AES_BLOCK_LOAD(padded_prefix), k2keys[0]); + for (i = 1; i < ROUNDS; i++) { + e1 = AES_ENC(e1, k1keys[i]); + e2 = AES_ENC(e2, k2keys[i]); + } + e1 = AES_ENC(e1, k1keys[ROUNDS]); + e2 = AES_ENC(e2, k2keys[ROUNDS]); + + e = AES_BLOCK_XOR(e1, e2); + AES_BLOCK_STORE(t, e); + + cipher_bit = t[15] & 1; + bit_pos = 127 - prefix_len_bits; + original_bit = pfx_get_bit(in, bit_pos); + pfx_set_bit(encrypted, bit_pos, original_bit ^ cipher_bit); + + pfx_shift_left(padded_prefix); + pfx_set_bit(padded_prefix, 0, original_bit); + } + + memcpy(out, encrypted, 16); +} + +static void +pfx_decrypt(uint8_t *out, const uint8_t *in, const uint8_t *k) +{ + KeySchedule k1keys; + KeySchedule k2keys; + uint8_t diff[16]; + uint8_t decrypted[16]; + uint8_t padded_prefix[16]; + uint8_t t[16]; + aes_block_t e1, e2, e; + unsigned int prefix_start = 0; + unsigned int prefix_len_bits; + unsigned int bit_pos; + uint8_t cipher_bit; + uint8_t encrypted_bit; + uint8_t original_bit; + size_t i; + uint8_t d; + + expand_key(k1keys, k); + expand_key(k2keys, k + 16); + + AES_BLOCK_STORE(diff, AES_BLOCK_XOR(k1keys[ROUNDS / 2], k2keys[ROUNDS / 2])); + d = 0; + for (i = 0; i < 16; i++) { + d |= diff[i]; + } + if (d == 0) { + for (i = 0; i < 16; i++) { + diff[i] = k[i] ^ 0x5a; + } + expand_key(k2keys, diff); + } + + if (is_ipv4_mapped(in)) { + prefix_start = 96; + } + + pfx_pad_prefix(padded_prefix, prefix_start); + + memset(decrypted, 0, 16); + if (prefix_start == 96) { + decrypted[10] = 0xff; + decrypted[11] = 0xff; + } + + for (prefix_len_bits = prefix_start; prefix_len_bits < 128; prefix_len_bits++) { + e1 = AES_BLOCK_XOR(AES_BLOCK_LOAD(padded_prefix), k1keys[0]); + e2 = AES_BLOCK_XOR(AES_BLOCK_LOAD(padded_prefix), k2keys[0]); + for (i = 1; i < ROUNDS; i++) { + e1 = AES_ENC(e1, k1keys[i]); + e2 = AES_ENC(e2, k2keys[i]); + } + e1 = AES_ENC(e1, k1keys[ROUNDS]); + e2 = AES_ENC(e2, k2keys[ROUNDS]); + + e = AES_BLOCK_XOR(e1, e2); + AES_BLOCK_STORE(t, e); + + cipher_bit = t[15] & 1; + bit_pos = 127 - prefix_len_bits; + encrypted_bit = pfx_get_bit(in, bit_pos); + original_bit = encrypted_bit ^ cipher_bit; + pfx_set_bit(decrypted, bit_pos, original_bit); + + pfx_shift_left(padded_prefix); + pfx_set_bit(padded_prefix, 0, original_bit); + } + + memcpy(out, decrypted, 16); +} + struct ipcrypt_implementation ipcrypt_soft_implementation = { SODIUM_C99(.encrypt =) encrypt, SODIUM_C99(.decrypt =) decrypt, SODIUM_C99(.nd_encrypt =) nd_encrypt, SODIUM_C99(.nd_decrypt =) nd_decrypt, - SODIUM_C99(.ndx_encrypt =) ndx_encrypt, SODIUM_C99(.ndx_decrypt =) ndx_decrypt + SODIUM_C99(.ndx_encrypt =) ndx_encrypt, SODIUM_C99(.ndx_decrypt =) ndx_decrypt, + SODIUM_C99(.pfx_encrypt =) pfx_encrypt, SODIUM_C99(.pfx_decrypt =) pfx_decrypt }; diff --git a/src/libsodium/include/sodium/crypto_ipcrypt.h b/src/libsodium/include/sodium/crypto_ipcrypt.h index 715ba03e..8b2271ed 100644 --- a/src/libsodium/include/sodium/crypto_ipcrypt.h +++ b/src/libsodium/include/sodium/crypto_ipcrypt.h @@ -52,6 +52,14 @@ size_t crypto_ipcrypt_ndx_inputbytes(void); SODIUM_EXPORT size_t crypto_ipcrypt_ndx_outputbytes(void); +#define crypto_ipcrypt_PFX_KEYBYTES 32U +SODIUM_EXPORT +size_t crypto_ipcrypt_pfx_keybytes(void); + +#define crypto_ipcrypt_PFX_BYTES 16U +SODIUM_EXPORT +size_t crypto_ipcrypt_pfx_bytes(void); + SODIUM_EXPORT void crypto_ipcrypt_keygen(unsigned char k[crypto_ipcrypt_KEYBYTES]) __attribute__((nonnull)); @@ -59,6 +67,10 @@ SODIUM_EXPORT void crypto_ipcrypt_ndx_keygen(unsigned char k[crypto_ipcrypt_NDX_KEYBYTES]) __attribute__((nonnull)); +SODIUM_EXPORT +void crypto_ipcrypt_pfx_keygen(unsigned char k[crypto_ipcrypt_PFX_KEYBYTES]) + __attribute__((nonnull)); + SODIUM_EXPORT void crypto_ipcrypt_encrypt(unsigned char out[crypto_ipcrypt_BYTES], const unsigned char in[crypto_ipcrypt_BYTES], @@ -97,6 +109,18 @@ void crypto_ipcrypt_ndx_decrypt(unsigned char out[crypto_ipcrypt_NDX_INPUT const unsigned char k[crypto_ipcrypt_NDX_KEYBYTES]) __attribute__((nonnull)); +SODIUM_EXPORT +void crypto_ipcrypt_pfx_encrypt(unsigned char out[crypto_ipcrypt_PFX_BYTES], + const unsigned char in[crypto_ipcrypt_PFX_BYTES], + const unsigned char k[crypto_ipcrypt_PFX_KEYBYTES]) + __attribute__((nonnull)); + +SODIUM_EXPORT +void crypto_ipcrypt_pfx_decrypt(unsigned char out[crypto_ipcrypt_PFX_BYTES], + const unsigned char in[crypto_ipcrypt_PFX_BYTES], + const unsigned char k[crypto_ipcrypt_PFX_KEYBYTES]) + __attribute__((nonnull)); + #ifdef __cplusplus } #endif diff --git a/test/default/ipcrypt.c b/test/default/ipcrypt.c index 198c5b13..fe57b1bf 100644 --- a/test/default/ipcrypt.c +++ b/test/default/ipcrypt.c @@ -15,13 +15,17 @@ main(void) { unsigned char key[crypto_ipcrypt_KEYBYTES]; unsigned char ndx_key[crypto_ipcrypt_NDX_KEYBYTES]; + unsigned char pfx_key[crypto_ipcrypt_PFX_KEYBYTES]; unsigned char input[crypto_ipcrypt_BYTES]; unsigned char output[crypto_ipcrypt_BYTES]; unsigned char nd_output[crypto_ipcrypt_ND_OUTPUTBYTES]; unsigned char ndx_output[crypto_ipcrypt_NDX_OUTPUTBYTES]; + unsigned char pfx_output[crypto_ipcrypt_PFX_BYTES]; unsigned char tweak_nd[crypto_ipcrypt_ND_TWEAKBYTES]; unsigned char tweak_ndx[crypto_ipcrypt_NDX_TWEAKBYTES]; unsigned char decrypted[crypto_ipcrypt_BYTES]; + unsigned char encrypted1[crypto_ipcrypt_PFX_BYTES]; + unsigned char encrypted2[crypto_ipcrypt_PFX_BYTES]; size_t i; printf("crypto_ipcrypt_BYTES: %zu\n", crypto_ipcrypt_bytes()); @@ -34,6 +38,8 @@ main(void) printf("crypto_ipcrypt_NDX_TWEAKBYTES: %zu\n", crypto_ipcrypt_ndx_tweakbytes()); printf("crypto_ipcrypt_NDX_INPUTBYTES: %zu\n", crypto_ipcrypt_ndx_inputbytes()); printf("crypto_ipcrypt_NDX_OUTPUTBYTES: %zu\n", crypto_ipcrypt_ndx_outputbytes()); + printf("crypto_ipcrypt_PFX_KEYBYTES: %zu\n", crypto_ipcrypt_pfx_keybytes()); + printf("crypto_ipcrypt_PFX_BYTES: %zu\n", crypto_ipcrypt_pfx_bytes()); /* Test 1: Format-preserving encryption with known key/input */ memset(key, 0x00, sizeof key); @@ -197,6 +203,115 @@ main(void) } printf("OK: In-place round-trip successful\n"); + /* Test 8: Prefix-preserving encryption (PFX mode) */ + printf("\nTest 8: Prefix-preserving encryption (PFX mode)\n"); + + /* Test vector from the specification: + * Key: 0123456789abcdeffedcba98765432101032547698badcfeefcdab8967452301 + * Input IP: 0.0.0.0 (IPv4-mapped) + * Expected: 151.82.155.134 + */ + sodium_hex2bin(pfx_key, sizeof pfx_key, + "0123456789abcdeffedcba98765432101032547698badcfeefcdab8967452301", + 64, NULL, NULL, NULL); + + memset(input, 0, sizeof input); + input[10] = 0xff; + input[11] = 0xff; + /* 0.0.0.0 */ + + printf("PFX Key: "); + dump_hex(pfx_key, sizeof pfx_key); + printf("Input (0.0.0.0): "); + dump_hex(input, sizeof input); + + crypto_ipcrypt_pfx_encrypt(pfx_output, input, pfx_key); + printf("PFX Encrypted: "); + dump_hex(pfx_output, sizeof pfx_output); + + crypto_ipcrypt_pfx_decrypt(decrypted, pfx_output, pfx_key); + printf("PFX Decrypted: "); + dump_hex(decrypted, sizeof decrypted); + + if (memcmp(input, decrypted, sizeof input) != 0) { + printf("FAILED: PFX decrypted does not match input\n"); + return 1; + } + printf("OK: PFX round-trip successful\n"); + + /* Test 9: Verify prefix preservation - IPs in same /24 should share encrypted prefix */ + printf("\nTest 9: Verify prefix preservation\n"); + + /* Use test vector key from spec */ + sodium_hex2bin(pfx_key, sizeof pfx_key, + "2b7e151628aed2a6abf7158809cf4f3ca9f5ba40db214c3798f2e1c23456789a", + 64, NULL, NULL, NULL); + + /* 10.0.0.47 */ + memset(input, 0, sizeof input); + input[10] = 0xff; + input[11] = 0xff; + input[12] = 10; + input[13] = 0; + input[14] = 0; + input[15] = 47; + + crypto_ipcrypt_pfx_encrypt(encrypted1, input, pfx_key); + printf("10.0.0.47 encrypted: "); + dump_hex(encrypted1, sizeof encrypted1); + + /* 10.0.0.129 */ + input[15] = 129; + + crypto_ipcrypt_pfx_encrypt(encrypted2, input, pfx_key); + printf("10.0.0.129 encrypted: "); + dump_hex(encrypted2, sizeof encrypted2); + + /* Check that the first 24 bits of the encrypted IPv4 addresses match (bytes 12-14) */ + if (memcmp(encrypted1 + 12, encrypted2 + 12, 3) != 0) { + printf("FAILED: Prefix not preserved for /24 addresses\n"); + return 1; + } + printf("OK: /24 prefix preserved\n"); + + /* Test 10: PFX keygen */ + printf("\nTest 10: PFX key generation\n"); + crypto_ipcrypt_pfx_keygen(pfx_key); + printf("Random PFX key generated (skipped in output)\n"); + + /* Test 11: IPv6 prefix preservation */ + printf("\nTest 11: IPv6 prefix-preserving encryption\n"); + + sodium_hex2bin(pfx_key, sizeof pfx_key, + "2b7e151628aed2a6abf7158809cf4f3ca9f5ba40db214c3798f2e1c23456789a", + 64, NULL, NULL, NULL); + + /* 2001:db8::1 */ + memset(input, 0, sizeof input); + input[0] = 0x20; + input[1] = 0x01; + input[2] = 0x0d; + input[3] = 0xb8; + input[15] = 0x01; + + crypto_ipcrypt_pfx_encrypt(encrypted1, input, pfx_key); + printf("2001:db8::1 encrypted: "); + dump_hex(encrypted1, sizeof encrypted1); + + /* 2001:db8::2 */ + input[15] = 0x02; + + crypto_ipcrypt_pfx_encrypt(encrypted2, input, pfx_key); + printf("2001:db8::2 encrypted: "); + dump_hex(encrypted2, sizeof encrypted2); + + /* Check that the first 64 bits match (bytes 0-7 for /64 prefix) */ + if (memcmp(encrypted1, encrypted2, 8) != 0) { + printf("FAILED: IPv6 /64 prefix not preserved\n"); + return 1; + } + printf("OK: IPv6 /64 prefix preserved\n"); + printf("\nAll tests passed!\n"); return 0; diff --git a/test/default/ipcrypt.exp b/test/default/ipcrypt.exp index f3fd91fa..8ba7e879 100644 --- a/test/default/ipcrypt.exp +++ b/test/default/ipcrypt.exp @@ -8,6 +8,8 @@ crypto_ipcrypt_NDX_KEYBYTES: 32 crypto_ipcrypt_NDX_TWEAKBYTES: 16 crypto_ipcrypt_NDX_INPUTBYTES: 16 crypto_ipcrypt_NDX_OUTPUTBYTES: 32 +crypto_ipcrypt_PFX_KEYBYTES: 32 +crypto_ipcrypt_PFX_BYTES: 16 Test 1: Format-preserving encryption Key: 0102030405060708090a0b0c0d0e0f10 @@ -49,4 +51,24 @@ OK: Deterministic encryption verified Test 7: In-place encryption and decryption OK: In-place round-trip successful +Test 8: Prefix-preserving encryption (PFX mode) +PFX Key: 0123456789abcdeffedcba98765432101032547698badcfeefcdab8967452301 +Input (0.0.0.0): 00000000000000000000ffff00000000 +PFX Encrypted: 00000000000000000000ffff97529b86 +PFX Decrypted: 00000000000000000000ffff00000000 +OK: PFX round-trip successful + +Test 9: Verify prefix preservation +10.0.0.47 encrypted: 00000000000000000000ffff13d6d2f4 +10.0.0.129 encrypted: 00000000000000000000ffff13d6d250 +OK: /24 prefix preserved + +Test 10: PFX key generation +Random PFX key generated (skipped in output) + +Test 11: IPv6 prefix-preserving encryption +2001:db8::1 encrypted: 7cec702c12430f70d5ff5bae0021b09b +2001:db8::2 encrypted: 7cec702c12430f70d5ff5bae0021b098 +OK: IPv6 /64 prefix preserved + All tests passed! diff --git a/test/symbols/all-symbols.txt b/test/symbols/all-symbols.txt index 0a629993..44f12023 100644 --- a/test/symbols/all-symbols.txt +++ b/test/symbols/all-symbols.txt @@ -204,6 +204,7 @@ crypto_core_ed25519_add crypto_core_ed25519_bytes crypto_core_ed25519_from_string crypto_core_ed25519_from_string_ro +crypto_core_ed25519_from_uniform crypto_core_ed25519_hashbytes crypto_core_ed25519_is_valid_point crypto_core_ed25519_nonreducedscalarbytes @@ -230,6 +231,12 @@ crypto_core_hsalsa20_constbytes crypto_core_hsalsa20_inputbytes crypto_core_hsalsa20_keybytes crypto_core_hsalsa20_outputbytes +crypto_core_keccak1600_extract_bytes +crypto_core_keccak1600_init +crypto_core_keccak1600_permute_12 +crypto_core_keccak1600_permute_24 +crypto_core_keccak1600_statebytes +crypto_core_keccak1600_xor_bytes crypto_core_ristretto255_add crypto_core_ristretto255_bytes crypto_core_ristretto255_from_hash @@ -310,6 +317,29 @@ crypto_hash_sha512_final crypto_hash_sha512_init crypto_hash_sha512_statebytes crypto_hash_sha512_update +crypto_ipcrypt_bytes +crypto_ipcrypt_decrypt +crypto_ipcrypt_encrypt +crypto_ipcrypt_keybytes +crypto_ipcrypt_keygen +crypto_ipcrypt_nd_decrypt +crypto_ipcrypt_nd_encrypt +crypto_ipcrypt_nd_inputbytes +crypto_ipcrypt_nd_keybytes +crypto_ipcrypt_nd_outputbytes +crypto_ipcrypt_nd_tweakbytes +crypto_ipcrypt_ndx_decrypt +crypto_ipcrypt_ndx_encrypt +crypto_ipcrypt_ndx_inputbytes +crypto_ipcrypt_ndx_keybytes +crypto_ipcrypt_ndx_keygen +crypto_ipcrypt_ndx_outputbytes +crypto_ipcrypt_ndx_tweakbytes +crypto_ipcrypt_pfx_bytes +crypto_ipcrypt_pfx_decrypt +crypto_ipcrypt_pfx_encrypt +crypto_ipcrypt_pfx_keybytes +crypto_ipcrypt_pfx_keygen crypto_kdf_blake2b_bytes_max crypto_kdf_blake2b_bytes_min crypto_kdf_blake2b_contextbytes @@ -342,6 +372,16 @@ crypto_kdf_hkdf_sha512_statebytes crypto_kdf_keybytes crypto_kdf_keygen crypto_kdf_primitive +crypto_kem_mlkem768_ciphertextbytes +crypto_kem_mlkem768_dec +crypto_kem_mlkem768_enc +crypto_kem_mlkem768_enc_deterministic +crypto_kem_mlkem768_keypair +crypto_kem_mlkem768_publickeybytes +crypto_kem_mlkem768_secretkeybytes +crypto_kem_mlkem768_seed_keypair +crypto_kem_mlkem768_seedbytes +crypto_kem_mlkem768_sharedsecretbytes crypto_kx_client_session_keys crypto_kx_keypair crypto_kx_primitive @@ -545,6 +585,7 @@ crypto_sign_bytes crypto_sign_detached crypto_sign_ed25519 crypto_sign_ed25519_bytes +crypto_sign_ed25519_contextbytes_max crypto_sign_ed25519_detached crypto_sign_ed25519_keypair crypto_sign_ed25519_messagebytes_max @@ -559,6 +600,8 @@ crypto_sign_ed25519_sk_to_pk crypto_sign_ed25519_sk_to_seed crypto_sign_ed25519_verify_detached crypto_sign_ed25519ph_final_create +crypto_sign_ed25519ph_final_ctx_create +crypto_sign_ed25519ph_final_ctx_verify crypto_sign_ed25519ph_final_verify crypto_sign_ed25519ph_init crypto_sign_ed25519ph_statebytes @@ -639,6 +682,38 @@ crypto_verify_32 crypto_verify_32_bytes crypto_verify_64 crypto_verify_64_bytes +crypto_xof_shake128 +crypto_xof_shake128_blockbytes +crypto_xof_shake128_domain_standard +crypto_xof_shake128_init +crypto_xof_shake128_init_with_domain +crypto_xof_shake128_squeeze +crypto_xof_shake128_statebytes +crypto_xof_shake128_update +crypto_xof_shake256 +crypto_xof_shake256_blockbytes +crypto_xof_shake256_domain_standard +crypto_xof_shake256_init +crypto_xof_shake256_init_with_domain +crypto_xof_shake256_squeeze +crypto_xof_shake256_statebytes +crypto_xof_shake256_update +crypto_xof_turboshake128 +crypto_xof_turboshake128_blockbytes +crypto_xof_turboshake128_domain_standard +crypto_xof_turboshake128_init +crypto_xof_turboshake128_init_with_domain +crypto_xof_turboshake128_squeeze +crypto_xof_turboshake128_statebytes +crypto_xof_turboshake128_update +crypto_xof_turboshake256 +crypto_xof_turboshake256_blockbytes +crypto_xof_turboshake256_domain_standard +crypto_xof_turboshake256_init +crypto_xof_turboshake256_init_with_domain +crypto_xof_turboshake256_squeeze +crypto_xof_turboshake256_statebytes +crypto_xof_turboshake256_update escrypt_PBKDF2_SHA256 escrypt_alloc_region escrypt_free_local @@ -671,6 +746,16 @@ ge25519_p3_tobytes ge25519_scalarmult ge25519_scalarmult_base ge25519_tobytes +keccak1600_ref_extract_bytes +keccak1600_ref_init +keccak1600_ref_permute_12 +keccak1600_ref_permute_24 +keccak1600_ref_xor_bytes +mlkem768_ref_dec +mlkem768_ref_enc +mlkem768_ref_enc_deterministic +mlkem768_ref_keypair +mlkem768_ref_seed_keypair randombytes randombytes_buf randombytes_buf_deterministic @@ -689,6 +774,16 @@ sc25519_is_canonical sc25519_mul sc25519_muladd sc25519_reduce +shake128_ref +shake128_ref_init +shake128_ref_init_with_domain +shake128_ref_squeeze +shake128_ref_update +shake256_ref +shake256_ref_init +shake256_ref_init_with_domain +shake256_ref_squeeze +shake256_ref_update sodium_add sodium_allocarray sodium_base642bin @@ -739,4 +834,20 @@ sodium_stream_salsa20_xmm6_xor_ic sodium_sub sodium_unpad sodium_version_string +softaes_block_decrypt softaes_block_encrypt +softaes_expand_key128 +softaes_expand_key256 +softaes_inv_mix_columns +softaes_invert_key_schedule128 +softaes_invert_key_schedule256 +turboshake128_ref +turboshake128_ref_init +turboshake128_ref_init_with_domain +turboshake128_ref_squeeze +turboshake128_ref_update +turboshake256_ref +turboshake256_ref_init +turboshake256_ref_init_with_domain +turboshake256_ref_squeeze +turboshake256_ref_update