---
# System prepended metadata

title: Computation Integrity Design Doc

---

# Computation Integrity Design Doc

## In Enclave

### structs
```
EncryptedBlock {
  block_ciphertext; // the encrypted block
  block_MAC; // AES-GCM authentication tag over the block
}
```

```
EncryptedBlocks{
  [EncryptedBlock]; // list of encrypted blocks
}
```

```
LogEntry {
  string ecall; // the current ecall
  int jobID; // to prevent replay attacks
  // list of EncryptedBlock MACs for this output partition
  bytes mac_lst[]; // [block_MAC_1, block_MAC_2, ..., block_MAC_n] for all blocks going to a particular destination partition;
  bytes global_mac; // MAC(mac_lst)
  LogEntry input_log_entries[]; // List of all input log entries to this ecall
}
```

```
EnclaveContext {
  // Retains state of entire enclave, one per executor
  LogEntry enclave_operator_log[];

  current_job_id; // counter that increments after every ecall; serves as protection against replay attacks, e.g. driver -> executor
}
```

### Functions
Modify each ecall operator to match the following
```
ecall_operator(encrypted_blocks[], input_log_entries[], input_log_entries_macs[])
"""
  Params
  ------
  encrypted_blocks : encrypted input; each block has a corresponding tag
  input_log_entries : list of log entries, one per input data source   partition
  input_log_entries_macs : list of MACs, MAC(input_log_entry) for input_log_entry in input_log_entries

  Returns
  -------
  [(EncryptedBlocks, LogEntry)], one tuple per destination partition
"""
// Verify that job ID of all log entries match EnclaveContext.current_job_id
for i = 0 to input_log_entries.size() {
  assert input_log_entries[i].jobID = EnclaveContext.current_job_id
}

// Verify that MACs over log entries are correct to ensure that individual log_entries have not been tampered with
for i = 0 to input_log_entries.size() {
  assert input_log_entries_macs[i] = MAC(input_log_entries[i])
}

// Verify that global_mac in each log_entry is correct to ensure that mac_lst has not been tampered with
for log_entry in input_log_entries {
  assert log_entry.global_mac = MAC(log_entry.mac_lst)
}

// Group input encrypted_blocks by source partition using the mac_lst of input_log_entries
// For each source partition $p$, verify that the corresponding input_log_entry.global_mac = MAC([encrypted_blocks_from_$p$_MACs]) 
// to ensure that blocks came from where input_log_entries said they came from and to verify that data shuffling has occurred properly

// Verify that each EncryptedBlock's MAC tag is correct
// Original ecall logic, producing some encrypted blocks

// Create 1 log entry for this operation per destination partition, incrementing job ID
LogEntry new_log_entries[num_dest_partitions];

// Add these log_entries to EnclaveContext.enclave_operator_log
EnclaveContext.enclave_operator_log.push_back(new_log_entries)

// Increment job ID counter in EnclaveContext
EnclaveContext.current_job_id++;
```

## Job Verification Engine
Once the entire Spark job has finished, each executor will end up with an `enclave_operator_log`, with the last item in the log essentially a rolled-up version of the entire log (the last ecall won't result in a shuffle, so it will only add 1 log entry to the enclave operator log). Every executor sends the last item in its log to the Job Verification Engine. The Job Verification Engine unrolls all logs, enabling it to recreate the exact ecalls executed at each executor and the exact data movement patterns during the entire job. The engine can then condense ecalls as necessary into physical operators, producing a DAG. The engine lastly compares the recreated DAG against one produced by a query planner replica sitting within a trusted zone to verify computation integrity.
