<commit_before>-- Implementation of ESP over IPv6 using AES-128-GCM using a 12 byte ICV and
-- “Extended Sequence Number” (see RFC 4303 and RFC 4106).
--
-- Notes:
--
--  * Auditing is *not* implemented, see the “Auditing” section of RFC 4303 for
--    details: https://tools.ietf.org/html/rfc4303#section-4
--
--  * Anti-replay protection for packets within `window_size' on the receiver
--    side is *not* implemented, see `track_seq_no.c'.
--
--  * Recovery from synchronisation loss is is *not* implemented, see
--    Appendix 3: “Handling Loss of Synchronization due to Significant Packet
--    Loss” of RFC 4303 for details: https://tools.ietf.org/html/rfc4303#page-42
--
--  * Wrapping around of the Extended Sequence Number is *not* detected because
--    it is assumed to be an unrealistic scenario as it would take 584 years to
--    overflow the counter when transmitting 10^9 packets per second.
--
--  * Rejection of IP fragments is *not* implemented because
--    `lib.protocol.ipv6' does not support fragmentation. E.g. fragments will
--    be rejected because they can not be parsed as IPv6 packets. If however
--    `lib.protocol.ipv6' were to be updated to be able to parse IP fragments
--    this implementation would have to be updated as well to remain correct.
--    See the “Reassembly” section of RFC 4303 for details:
--    https://tools.ietf.org/html/rfc4303#section-3.4.1
--
module(..., package.seeall)
local header = require("lib.protocol.header")
local datagram = require("lib.protocol.datagram")
local ethernet = require("lib.protocol.ethernet")
local ipv6 = require("lib.protocol.ipv6")
local esp = require("lib.protocol.esp")
local esp_tail = require("lib.protocol.esp_tail")
local aes_128_gcm = require("lib.ipsec.aes_128_gcm")
local seq_no_t = require("lib.ipsec.seq_no_t")
local lib = require("core.lib")
local ffi = require("ffi")
local C = ffi.C
require("lib.ipsec.track_seq_no_h")


local ETHERNET_SIZE = ethernet:sizeof()
local IPV6_SIZE = ipv6:sizeof()
local PAYLOAD_OFFSET = ETHERNET_SIZE + IPV6_SIZE
local ESP_NH = 50 -- https://tools.ietf.org/html/rfc4303#section-2
local ESP_SIZE = esp:sizeof()
local ESP_TAIL_SIZE = esp_tail:sizeof()

function esp_v6_new (conf)
   assert(conf.mode == "aes-128-gcm", "Only supports aes-128-gcm.")
   assert(conf.spi, "Need SPI.")
   local gcm = aes_128_gcm:new(conf.spi, conf.keymat, conf.salt)
   local o = {}
   o.ESP_OVERHEAD = ESP_SIZE + ESP_TAIL_SIZE + gcm.IV_SIZE + gcm.AUTH_SIZE
   o.aes_128_gcm = gcm
   o.spi = conf.spi
   o.seq = ffi.new(seq_no_t)
   o.pad_to = 4 -- minimal padding
   o.ip = ipv6:new({})
   o.esp = esp:new({})
   o.esp_tail = esp_tail:new({})
   return o
end

esp_v6_encrypt = {}

function esp_v6_encrypt:new (conf)
   local o = esp_v6_new(conf)
   o.ESP_PAYLOAD_OVERHEAD =  o.aes_128_gcm.IV_SIZE + ESP_TAIL_SIZE
   return setmetatable(o, {__index=esp_v6_encrypt})
end

-- Increment sequence number.
function esp_v6_encrypt:next_seq_no ()
   self.seq.no = self.seq.no + 1
end

local function padding (a, l) return (a - l%a) % a end

-- Encapsulation is performed as follows:
--   1. Grow p to fit ESP overhead
--   2. Append ESP trailer to p
--   3. Encrypt payload+trailer in place
--   4. Move resulting ciphertext to make room for ESP header
--   5. Write ESP header
function esp_v6_encrypt:encapsulate (p)
   local gcm = self.aes_128_gcm
   local data, length = packet.data(p), packet.length(p)
   local payload = data + PAYLOAD_OFFSET
   local payload_length = length - PAYLOAD_OFFSET
   -- Padding, see https://tools.ietf.org/html/rfc4303#section-2.4
   local pad_length = padding(self.pad_to, payload_length + self.ESP_PAYLOAD_OVERHEAD)
   local overhead = self.ESP_OVERHEAD + pad_length
   packet.resize(p, length + overhead)
   local padding_start = data + length
   ffi.fill(padding_start, pad_length)
   self.ip:new_from_mem(data + ETHERNET_SIZE, IPV6_SIZE)
   self.esp_tail:new_from_mem(padding_start + pad_length, ESP_TAIL_SIZE)
   self.esp_tail:next_header(self.ip:next_header())
   self.esp_tail:pad_length(pad_length)
   self:next_seq_no()
   local ptext_length = payload_length + pad_length + ESP_TAIL_SIZE
   gcm:encrypt(payload, self.seq, self.seq, payload, ptext_length)
   local iv = payload + ESP_SIZE<commit_msg>lib.ipsec.esp: Omit obsolete padding fill.<commit_after>-- Implementation of ESP over IPv6 using AES-128-GCM using a 12 byte ICV and
-- “Extended Sequence Number” (see RFC 4303 and RFC 4106).
--
-- Notes:
--
--  * Auditing is *not* implemented, see the “Auditing” section of RFC 4303 for
--    details: https://tools.ietf.org/html/rfc4303#section-4
--
--  * Anti-replay protection for packets within `window_size' on the receiver
--    side is *not* implemented, see `track_seq_no.c'.
--
--  * Recovery from synchronisation loss is is *not* implemented, see
--    Appendix 3: “Handling Loss of Synchronization due to Significant Packet
--    Loss” of RFC 4303 for details: https://tools.ietf.org/html/rfc4303#page-42
--
--  * Wrapping around of the Extended Sequence Number is *not* detected because
--    it is assumed to be an unrealistic scenario as it would take 584 years to
--    overflow the counter when transmitting 10^9 packets per second.
--
--  * Rejection of IP fragments is *not* implemented because
--    `lib.protocol.ipv6' does not support fragmentation. E.g. fragments will
--    be rejected because they can not be parsed as IPv6 packets. If however
--    `lib.protocol.ipv6' were to be updated to be able to parse IP fragments
--    this implementation would have to be updated as well to remain correct.
--    See the “Reassembly” section of RFC 4303 for details:
--    https://tools.ietf.org/html/rfc4303#section-3.4.1
--
module(..., package.seeall)
local header = require("lib.protocol.header")
local datagram = require("lib.protocol.datagram")
local ethernet = require("lib.protocol.ethernet")
local ipv6 = require("lib.protocol.ipv6")
local esp = require("lib.protocol.esp")
local esp_tail = require("lib.protocol.esp_tail")
local aes_128_gcm = require("lib.ipsec.aes_128_gcm")
local seq_no_t = require("lib.ipsec.seq_no_t")
local lib = require("core.lib")
local ffi = require("ffi")
local C = ffi.C
require("lib.ipsec.track_seq_no_h")


local ETHERNET_SIZE = ethernet:sizeof()
local IPV6_SIZE = ipv6:sizeof()
local PAYLOAD_OFFSET = ETHERNET_SIZE + IPV6_SIZE
local ESP_NH = 50 -- https://tools.ietf.org/html/rfc4303#section-2
local ESP_SIZE = esp:sizeof()
local ESP_TAIL_SIZE = esp_tail:sizeof()

function esp_v6_new (conf)
   assert(conf.mode == "aes-128-gcm", "Only supports aes-128-gcm.")
   assert(conf.spi, "Need SPI.")
   local gcm = aes_128_gcm:new(conf.spi, conf.keymat, conf.salt)
   local o = {}
   o.ESP_OVERHEAD = ESP_SIZE + ESP_TAIL_SIZE + gcm.IV_SIZE + gcm.AUTH_SIZE
   o.aes_128_gcm = gcm
   o.spi = conf.spi
   o.seq = ffi.new(seq_no_t)
   o.pad_to = 4 -- minimal padding
   o.ip = ipv6:new({})
   o.esp = esp:new({})
   o.esp_tail = esp_tail:new({})
   return o
end

esp_v6_encrypt = {}

function esp_v6_encrypt:new (conf)
   local o = esp_v6_new(conf)
   o.ESP_PAYLOAD_OVERHEAD =  o.aes_128_gcm.IV_SIZE + ESP_TAIL_SIZE
   return setmetatable(o, {__index=esp_v6_encrypt})
end

-- Increment sequence number.
function esp_v6_encrypt:next_seq_no ()
   self.seq.no = self.seq.no + 1
end

local function padding (a, l) return (a - l%a) % a end

-- Encapsulation is performed as follows:
--   1. Grow p to fit ESP overhead
--   2. Append ESP trailer to p
--   3. Encrypt payload+trailer in place
--   4. Move resulting ciphertext to make room for ESP header
--   5. Write ESP header
function esp_v6_encrypt:encapsulate (p)
   local gcm = self.aes_128_gcm
   local data, length = packet.data(p), packet.length(p)
   local payload = data + PAYLOAD_OFFSET
   local payload_length = length - PAYLOAD_OFFSET
   -- Padding, see https://tools.ietf.org/html/rfc4303#section-2.4
   local pad_length = padding(self.pad_to, payload_length + self.ESP_PAYLOAD_OVERHEAD)
   local overhead = self.ESP_OVERHEAD + pad_length
   packet.resize(p, length + overhead)
   self.ip:new_from_mem(data + ETHERNET_SIZE, IPV6_SIZE)
   self.esp_tail:new_from_mem(data + length + pad_length, ESP_TAIL_SIZE)
   self.esp_tail:next_header(self.ip:next_header())
   self.esp_tail:pad_length(pad_length)
   self:next_seq_no()
   local ptext_length = payload_length + pad_length + ESP_TAIL_SIZE
   gcm:encrypt(payload, self.seq, self.seq, payload, ptext_length)
   local iv = payload + ESP_SIZE
{'fraction_non_alphanumeric': 0.11508830152897949, 'fraction_numerical': 0.026549721464975702, 'mean_word_length': 3.802504268639727, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 10, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1033351', 'n_tokens_mistral': 3128, 'n_tokens_neox': 2950, 'n_words': 814}