Hi Liam,
1- Trace collection is done in hardware, on the CW-lite FPGA, so the time to do this is approximately the time for the collected traces to be observed, i.e. # of samples times sampling period (plus maybe a few clock cycles). You can then infer the trace transfer time by subtracting that trace collection time from the time measured to do:
trace = cw.capture_trace(scope, target, text, key)
This of course will also capture the time to go through all the layers from the Python code down to the USB driver. You’ll find the trace transfer time is typically significantly greater than the collection time.
2- The CPA tutorial targets the first round key (which is actually equal to the input key). This is not stated explicitly; it’s implied by the fact that the recovered key is the input key. In contrast, the CW305 FPGA tutorial for example, we need to do the following extra step to get from the recovered round key to the input key:
recv_key = key_schedule_rounds(recv_lastroundkey, 10, 0)
There are lots of resources for understanding the AES key expansion schedule:
Now, as for this line in our tutorial:
known_keys = np.asarray([trace.key for trace in traces]) # for fixed key, these keys are all the same
is simply building an array where the elements are the input AES key for each trace; in this case, every trace used the same key, so this is a bit superfluous (the original intent is probably that in the case where power traces are saved to a file for later analysis, it’s good practice to explicitly attach the key to every collected trace, to prevent confusion).
Again, what’s fixed for every trace here is the input key. Every 16-byte block of input plaintext is encrypted with that same key. For each of these blocks, 10 distinct round keys are used.
I commend you for wanting to get a more than basic understanding of AES! If you want to get a deep understanding, I recommend making your way through the FIPS standard. It may seem complicated, but it’s actually pretty simple, no advanced math required.
Jean-Pierre