Skip to content

Commit

Permalink
[capture] Add support for aes-fvsr-data capture
Browse files Browse the repository at this point in the history
Signed-off-by: Vladimir Rozic <[email protected]>
  • Loading branch information
nasahlpa committed May 9, 2024
1 parent 957e7c5 commit ab795d3
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 33 deletions.
63 changes: 46 additions & 17 deletions capture/capture_aes.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,22 +229,24 @@ def generate_ref_crypto(sample_fixed, mode, key, plaintext):
plaintext: The next plaintext.
key: The next key.
ciphertext: The next ciphertext.
sample_fixed: Is the next sample fixed or not?
"""
if mode == "aes_fvsr_key":
if sample_fixed:
plaintext, ciphertext, key = dg.get_fixed()
plaintext, ciphertext, key = dg.get_fixed('FVSR_KEY')
else:
plaintext, ciphertext, key = dg.get_random()
# The next sample is either fixed or random.
sample_fixed = plaintext[0] & 0x1
plaintext, ciphertext, key = dg.get_random('FVSR_KEY')
elif mode == "aes_fvsr_data":
if sample_fixed:
plaintext, ciphertext, key = dg.get_fixed('FVSR_DATA')
else:
plaintext, ciphertext, key = dg.get_random('FVSR_DATA')
else:
if mode == "aes_random":
cipher = AES.new(bytes(key), AES.MODE_ECB)
ciphertext_bytes = cipher.encrypt(bytes(plaintext))
ciphertext = [x for x in ciphertext_bytes]

return plaintext, key, ciphertext, sample_fixed
return plaintext, key, ciphertext


def check_ciphertext(ot_aes, expected_last_ciphertext, ciphertext_len):
Expand All @@ -271,11 +273,13 @@ def capture(scope: Scope, ot_aes: OTAES, capture_cfg: CaptureConfig,
project: SCAProject, target: Target):
""" Capture power consumption during AES encryption.
Supports four different capture types:
Supports six different capture types:
* aes_random: Fixed key, random plaintext.
* aes_random_batch: Fixed key, random plaintext in batch mode.
* aes_fvsr: Fixed vs. random key.
* aes_fvsr_batch: Fixed vs. random key batch.
* aes_fvsr_key: Fixed vs. random key.
* aes_fvsr_key_batch: Fixed vs. random key batch.
* aes_fvsr_data: Fixed vs. random data.
* aes_fvsr_data_batch: Fixed vs. random data batch.
Args:
scope: The scope class representing a scope (Husky or WaveRunner).
Expand All @@ -292,20 +296,25 @@ def capture(scope: Scope, ot_aes: OTAES, capture_cfg: CaptureConfig,
key = key_fixed
logger.info(f"Initializing OT AES with key {binascii.b2a_hex(bytes(key))} ...")
if capture_cfg.capture_mode == "aes_fvsr_key":
dg.set_start()
dg.set_start('FVSR_KEY')
elif capture_cfg.capture_mode == "aes_fvsr_data":
dg.set_start('FVSR_DATA')
else:
ot_aes.key_set(key)

# Generate plaintexts and keys for first batch.
if capture_cfg.batch_mode:
if capture_cfg.capture_mode == "aes_fvsr_key":
ot_aes.start_fvsr_batch_generate()
ot_aes.start_fvsr_batch_generate(1)
ot_aes.write_fvsr_batch_generate(capture_cfg.num_segments.to_bytes(4, "little"))
elif capture_cfg.capture_mode == "aes_fvsr_data":
ot_aes.start_fvsr_batch_generate(2)
elif capture_cfg.capture_mode == "aes_random":
ot_aes.batch_plaintext_set(text)

# FVSR setup.
sample_fixed = 1
prng_state = 0x99999999

# Optimization for CW trace library.
num_segments_storage = 1
Expand All @@ -326,21 +335,34 @@ def capture(scope: Scope, ot_aes: OTAES, capture_cfg: CaptureConfig,
# Fixed key, random plaintexts.
ot_aes.batch_alternative_encrypt(
capture_cfg.num_segments.to_bytes(4, "little"))
else:
elif capture_cfg.capture_mode == "aes_fvsr_key":
# Fixed vs random key test.
ot_aes.fvsr_key_batch_encrypt(
capture_cfg.num_segments.to_bytes(4, "little"))
else:
# Fixed vs random data test.
ot_aes.fvsr_data_batch_encrypt(
capture_cfg.num_segments.to_bytes(4, "little"))
else:
# Non batch mode.
if capture_cfg.capture_mode == "aes_fvsr_key":
if capture_cfg.capture_mode == "aes_fvsr_key" or \
capture_cfg.capture_mode == "aes_fvsr_data":
# Generate reference crypto material for aes_fvsr_key in non-batch mode
text, key, ciphertext, sample_fixed = generate_ref_crypto(
text, key, ciphertext = generate_ref_crypto(
sample_fixed = sample_fixed,
mode = capture_cfg.capture_mode,
key = key,
plaintext = text
)
if capture_cfg.capture_mode == "aes_fvsr_key":
sample_fixed = text[0] & 0x1
else:
sample_fixed = prng_state & 0x1
prng_state = prng_state >> 1
if sample_fixed:
prng_state ^= 0x80000057
ot_aes.key_set(key)

ot_aes.single_encrypt(text)

# Capture traces.
Expand All @@ -352,12 +374,19 @@ def capture(scope: Scope, ot_aes: OTAES, capture_cfg: CaptureConfig,
# Store traces
for i in range(capture_cfg.num_segments):
if capture_cfg.batch_mode or capture_cfg.capture_mode == "aes_random":
text, key, ciphertext, sample_fixed = generate_ref_crypto(
text, key, ciphertext = generate_ref_crypto(
sample_fixed = sample_fixed,
mode = capture_cfg.capture_mode,
key = key,
plaintext = text
)
sample_fixed = text[0] & 0x1
if capture_cfg.capture_mode == "aes_fvsr_data":
sample_fixed = prng_state & 0x1
prng_state = prng_state >> 1
if sample_fixed:
prng_state ^= 0x80000057

# Sanity check retrieved data (wave).
assert len(waves[i, :]) >= 1
# Store trace into database.
Expand All @@ -373,8 +402,6 @@ def capture(scope: Scope, ot_aes: OTAES, capture_cfg: CaptureConfig,

# Compare received ciphertext with generated.
compare_len = capture_cfg.output_len
if capture_cfg.batch_mode and capture_cfg.capture_mode == "aes_fvsr_key":
compare_len = 4
check_ciphertext(ot_aes, ciphertext, compare_len)

# Memory allocation optimization for CW trace library.
Expand Down Expand Up @@ -425,6 +452,8 @@ def main(argv=None):
mode = "aes_fvsr_key"
if "aes_random" in cfg["test"]["which_test"]:
mode = "aes_random"
elif "data" in cfg["test"]["which_test"]:
mode = "aes_fvsr_data"

# Setup the target, scope and project.
target, scope, project = setup(cfg, args.project)
Expand Down
2 changes: 2 additions & 0 deletions capture/configs/aes_sca_chip.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ test:
# which_test: aes_random
# which_test: aes_fvsr_key
# which_test: aes_fvsr_key_batch
# which_test: aes_fvsr_data
# which_test: aes_fvsr_data_batch
key_len_bytes: 16
text_len_bytes: 16
# These initial values are used only for random capture but not fixed-vs-random.
Expand Down
2 changes: 2 additions & 0 deletions capture/configs/aes_sca_cw305.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ test:
#which_test: aes_random_batch
#which_test: aes_fvsr_key
which_test: aes_fvsr_key_batch
# which_test: aes_fvsr_data
# which_test: aes_fvsr_data_batch
key_len_bytes: 16
text_len_bytes: 16
key_fixed: [0x81, 0x1E, 0x37, 0x31, 0xB0, 0x12, 0x0A, 0x78, 0x42, 0x78, 0x1E, 0x22, 0xB2, 0x5C, 0xDD, 0xF9]
Expand Down
2 changes: 2 additions & 0 deletions capture/configs/aes_sca_cw310.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ test:
# which_test: aes_random
# which_test: aes_fvsr_key
# which_test: aes_fvsr_key_batch
# which_test: aes_fvsr_data
# which_test: aes_fvsr_data_batch
key_len_bytes: 16
text_len_bytes: 16
# These initial values are used only for random capture but not fixed-vs-random.
Expand Down
23 changes: 20 additions & 3 deletions target/communication/sca_aes_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,11 +94,10 @@ def seed_lfsr(self, seed):
seed_data = {"seed": [x for x in seed]}
self.target.write(json.dumps(seed_data).encode("ascii"))

def start_fvsr_batch_generate(self):
def start_fvsr_batch_generate(self, command):
"""Set SW PRNG to starting values for FvsR data
generation.
"""
command = 1
if self.simple_serial:
self.target.write(cmd="d", data=command.to_bytes(4, "little"))
self.target.wait_ack()
Expand Down Expand Up @@ -166,7 +165,7 @@ def batch_encrypt(self, num_segments):
self.target.write(json.dumps(num_encryption_data).encode("ascii"))

def fvsr_key_batch_encrypt(self, num_segments):
""" Start batch encryption for FVSR.
""" Start batch encryption for FVSR key.
Args:
num_segments: Number of encryptions to perform.
"""
Expand All @@ -183,6 +182,24 @@ def fvsr_key_batch_encrypt(self, num_segments):
num_encryption_data = {"data": [x for x in num_segments]}
self.target.write(json.dumps(num_encryption_data).encode("ascii"))

def fvsr_data_batch_encrypt(self, num_segments):
""" Start batch encryption for FVSR data.
Args:
num_segments: Number of encryptions to perform.
"""
if self.simple_serial:
self.target.write(cmd = "h", data = num_segments)
self.target.wait_ack()
else:
# AesSca command.
self._ujson_aes_sca_cmd()
# FvsrKeyBatchEncrypt command.
self.target.write(json.dumps("FvsrDataBatchEncrypt").encode("ascii"))
# Number of encryptions.
time.sleep(0.01)
num_encryption_data = {"data": [x for x in num_segments]}
self.target.write(json.dumps(num_encryption_data).encode("ascii"))

def batch_plaintext_set(self, text, text_length: Optional[int] = 16):
""" Write plaintext to OpenTitan AES.
Expand Down
45 changes: 32 additions & 13 deletions util/data_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,25 @@ class data_generator():
def __init__(self):
self.cipher_gen = AES.new(bytes(self.key_generation), AES.MODE_ECB)

def set_start(self):
self.text_fixed = [0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA]
self.text_random = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]
self.key_fixed = [0x81, 0x1E, 0x37, 0x31, 0xB0, 0x12, 0x0A, 0x78, 0x42,
0x78, 0x1E, 0x22, 0xB2, 0x5C, 0xDD, 0xF9]
self.key_random = [0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53,
0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53]
def set_start(self, capture_type = 'FVSR_KEY'):
if capture_type == 'FVSR_KEY':
self.text_fixed = [0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA]
self.text_random = [0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC]
self.key_fixed = [0x81, 0x1E, 0x37, 0x31, 0xB0, 0x12, 0x0A, 0x78, 0x42,
0x78, 0x1E, 0x22, 0xB2, 0x5C, 0xDD, 0xF9]
self.key_random = [0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53,
0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53, 0x53]
else:
self.text_fixed = [0xDA, 0x39, 0xA3, 0xEE, 0x5E, 0x6B, 0x4B, 0x0D,
0x32, 0x55, 0xBF, 0xEF, 0x95, 0x60, 0x18, 0x90]
self.text_random = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
self.key_fixed = [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]
self.key_random = [0x01, 0x23, 0x45, 0x67, 0x89, 0xAB, 0xCD, 0xEF,
0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]

def advance_fixed(self):
text_fixed_bytes = self.cipher_gen.encrypt(bytes(self.text_fixed))
Expand All @@ -56,24 +66,33 @@ def advance_random(self):
# Convert bytearray into int array.
self.key_random = [x for x in key_random_bytes]

def get_fixed(self):
def advance_random_data(self):
text_random_bytes = self.cipher_gen.encrypt(bytes(self.text_random))
# Convert bytearray into int array.
self.text_random = [x for x in text_random_bytes]

def get_fixed(self, capture_type = 'FVSR_KEY'):
pt = self.text_fixed
key = self.key_fixed
cipher_fixed = AES.new(bytes(self.key_fixed), AES.MODE_ECB)
ct_bytes = cipher_fixed.encrypt(bytes(self.text_fixed))
ct = [x for x in ct_bytes]
del (cipher_fixed)
self.advance_fixed()
if capture_type == 'FVSR_KEY':
self.advance_fixed()
return pt, ct, key

def get_random(self):
def get_random(self, capture_type = 'FVSR_KEY'):
pt = self.text_random
key = self.key_random
cipher_random = AES.new(bytes(self.key_random), AES.MODE_ECB)
ct_bytes = cipher_random.encrypt(bytes(self.text_random))
ct = [x for x in ct_bytes]
del (cipher_random)
self.advance_random()
if capture_type == 'FVSR_KEY':
self.advance_random()
else:
self.advance_random_data()
return pt, ct, key

def get_kmac_fixed(self):
Expand Down

0 comments on commit ab795d3

Please sign in to comment.