padded_payload = security_param + payload + pad_separator + padding

len(security_param) = 16  # do we really need this?
len(pad_separator) = 1

max_padded_payload_size = 2048
max_payload_size 
  = max_padded_payload_size - len(security_param) - len(pad_separator)
  = 2031
  
max_layers = 3

##########

def new_header(
	initial_ephermeral_sk: X25519Privkey,
	layer_pks: list[X25519Pubkey],
):
	keysets = derive_keysets(initial_emphermeral_sk, layer_pks)
	fillers = TODO()
	routing_capsule = build_routing_capsule(keysets)
	

def derive_keysets(
	initial_ephermeral_sk: X25519Privkey,
	layer_pks: list[X25519Pubkey],
) -> list[KeySet]:
  dh_shared_keys = layer_pks.map(|pk| initial_ephermeral_sk.exchange(pk))
  keysets = dh_shared_keys.map(|k| derive_keyset(k))
  return initial_ephermeral_sk.public(), keysets
  
def derive_keyset(dh_shared_key) -> KeySet:
	key = HKDF-SHA256(dh_shared_key)
	assert len(key) == 256
	header_enckey = key[0:16]  # 16 bytes (128bits) for AES128
	header_hmac_key = key[16:32]  # 16 bytes for HMAC authn
	payload_key = key[32:244]  # 192 bytes for ChaCha20
	blinding_factor = key[224:]  # 32 bytes (NOT USED IN OURS) for deriving a shared key for a next mix node
	return KeySet(header_enckey, header_hmac_key, payload_key, blinding_factor)
	
def build_routing_capsule(keysets: list[KeySet]) -> RoutingCapsule:
	capsule = None
	for keyset in keysets:
		capsule = encapsule_routing(keyset, capsule)
	return capsule
	
class RoutingCapsule:
	encrypted_routing: bytes
	integrity_hmac: bytes

class Routing:
	# TODO: add more if necessary
	next_truncated_encrypted_routing: bytes
	
def encapsule_routing(keyset: KeySet, capsule: RoutingCapsule | None) -> RoutingCapsule:
	routing = Routing()