Skip to content

Commit

Permalink
[sival/aes] Add AES interrupt encryption test
Browse files Browse the repository at this point in the history
This commit implements the chip_sw_aes_interrupt_encryption test that is
defined in the chip testplan. The purpose of this test is to check,
whether an AES encryption can be interrupted and restored.

Tue Oct 22  Incorporated the changes suggested by reviewers and added AES modes (CFB-128, OFB, CTR) called out in the test plan
Tue Oct 23 Fixed lint errors
Signed-off-by: Ramesh Prakash <[email protected]>
  • Loading branch information
rprakas-gsc committed Oct 23, 2024
1 parent 7fad7db commit 26591f0
Show file tree
Hide file tree
Showing 2 changed files with 286 additions and 0 deletions.
25 changes: 25 additions & 0 deletions sw/device/tests/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,31 @@ opentitan_test(
],
)

opentitan_test(
name = "aes_interrupt_encryption_test",
srcs = ["aes_interrupt_encryption_test.c"],
exec_env = dicts.add(
{
"//hw/top_earlgrey:sim_verilator": None,
"//hw/top_earlgrey:fpga_cw340_sival": None,
},
),
verilator = verilator_params(
timeout = "long",
),
deps = [
"//hw/ip/aes:model",
"//hw/top_earlgrey/sw/autogen:top_earlgrey",
"//sw/device/lib/base:memory",
"//sw/device/lib/base:mmio",
"//sw/device/lib/dif:aes",
"//sw/device/lib/dif:edn",
"//sw/device/lib/runtime:log",
"//sw/device/lib/testing:aes_testutils",
"//sw/device/lib/testing/test_framework:ottf_main",
],
)

alias(
name = "aes_smoketest_entropy_testutils",
actual = select({
Expand Down
261 changes: 261 additions & 0 deletions sw/device/tests/aes_interrupt_encryption_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
// Copyright lowRISC contributors (OpenTitan project).
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

#include "hw/ip/aes/model/aes_modes.h"
#include "sw/device/lib/base/memory.h"
#include "sw/device/lib/base/mmio.h"
#include "sw/device/lib/base/multibits.h"
#include "sw/device/lib/dif/dif_aes.h"
#include "sw/device/lib/dif/dif_csrng.h"
#include "sw/device/lib/dif/dif_csrng_shared.h"
#include "sw/device/lib/dif/dif_edn.h"
#include "sw/device/lib/runtime/log.h"
#include "sw/device/lib/testing/aes_testutils.h"
#include "sw/device/lib/testing/csrng_testutils.h"
#include "sw/device/lib/testing/test_framework/check.h"
#include "sw/device/lib/testing/test_framework/ottf_main.h"

#include "hw/top_earlgrey/sw/autogen/top_earlgrey.h"

enum {
kTestTimeout = (1000 * 1000),
};

// The mask share, used to mask kKey. Note that the masking should not be done
// manually. Software is expected to get the key in two shares right from the
// beginning.

static const unsigned char kAesModesPlainTextBlock12[32] = {
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e,
0x11, 0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03,
0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
};

static const unsigned char kAesModesPlainTextBlock34[32] = {
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1,
0x19, 0x1a, 0x0a, 0x52, 0xef, 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f,
0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10};

static const uint8_t kKeyShare1[] = {
0x0f, 0x1f, 0x2f, 0x3f, 0x4f, 0x5f, 0x6f, 0x7f, 0x8f, 0x9f, 0xaf,
0xbf, 0xcf, 0xdf, 0xef, 0xff, 0x0a, 0x1a, 0x2a, 0x3a, 0x4a, 0x5a,
0x6a, 0x7a, 0x8a, 0x9a, 0xaa, 0xba, 0xca, 0xda, 0xea, 0xfa,
};

OTTF_DEFINE_TEST_CONFIG();

status_t execute_test(void) {
dif_aes_mode_t aes_mode;
dif_aes_iv_t iv_mode, iv;

for (int test_modes = 1; test_modes < 5; test_modes++) {
switch (test_modes) {
case 1:
LOG_INFO("Mode: CBC");
aes_mode = kDifAesModeCbc;
memcpy(iv_mode.iv, kAesModesIvCbc, sizeof(kAesModesIvCbc));
break;
case 2:
LOG_INFO("Mode: CFB-128");
aes_mode = kDifAesModeCfb;
memcpy(iv_mode.iv, kAesModesIvCfb, sizeof(kAesModesIvCfb));
break;
case 3:
LOG_INFO("Mode: OFB");
aes_mode = kDifAesModeOfb;
memcpy(iv_mode.iv, kAesModesIvOfb, sizeof(kAesModesIvOfb));
break;
case 4:
LOG_INFO("Mode: CTR");
aes_mode = kDifAesModeCtr;
memcpy(iv_mode.iv, kAesModesIvCtr, sizeof(kAesModesIvCtr));
break;
}
// Initialise AES.
dif_aes_t aes;
TRY(dif_aes_init(mmio_region_from_addr(TOP_EARLGREY_AES_BASE_ADDR), &aes));
TRY(dif_aes_reset(&aes));

// Mask the key. Note that this should not be done manually. Software is
// expected to get the key in two shares right from the beginning.

uint8_t key_share0[sizeof(kAesModesKey128)];
for (int i = 0; i < sizeof(kAesModesKey128); ++i) {
key_share0[i] = kAesModesKey128[i] ^ kKeyShare1[i];
}

// "Convert" key share byte arrays to `dif_aes_key_share_t`.
dif_aes_key_share_t key;
memcpy(key.share0, key_share0, sizeof(key.share0));
memcpy(key.share1, kKeyShare1, sizeof(key.share1));

// Write the initial key share, IV and data in CSRs (known combinations).

dif_aes_transaction_t transaction = {
.operation = kDifAesOperationEncrypt,
.mode = aes_mode,
.key_len = kDifAesKey128,
.key_provider = kDifAesKeySoftwareProvided,
.mask_reseeding = kDifAesReseedPerBlock,
.manual_operation = kDifAesManualOperationAuto,
.reseed_on_key_change = false,
.ctrl_aux_lock = false,
};

TRY(dif_aes_start(&aes, &transaction, &key, &iv_mode));
// "Convert" plain data byte arrays to `dif_aes_data_t` array.

enum {
kAesNumBlocks = 2,
};

dif_aes_data_t plain_text[kAesNumBlocks], plain_text_block34[kAesNumBlocks];
dif_aes_data_t cipher_text[kAesNumBlocks];
dif_aes_data_t cipher_text_block34[kAesNumBlocks];

memcpy(plain_text[0].data, kAesModesPlainTextBlock12,
sizeof(kAesModesPlainTextBlock12));

// Encrypt kAesNumBlocks blocks.
TRY(dif_aes_process_data(&aes, plain_text, cipher_text,
(size_t)kAesNumBlocks));
LOG_INFO("AES Encrypt of first two blocks done");

dif_aes_iv_t iv_encrypt1;
TRY(dif_aes_read_iv(&aes, &iv_encrypt1));

bool ready = false;

// Disable the AES unit to no longer automatically start
// encryption/decryption

transaction.manual_operation = kDifAesManualOperationManual;

TRY(dif_aes_trigger(&aes, kDifAesTriggerDataOutClear));
TRY(dif_aes_trigger(&aes, kDifAesTriggerKeyIvDataInClear));

LOG_INFO("De-initialized Automatic operation");

//*************** Perform an trial encryption using a different data and
// key. Message size, key size and mode can be chosen arbitrarily.

TRY(dif_aes_init(mmio_region_from_addr(TOP_EARLGREY_AES_BASE_ADDR), &aes));

// Mask the key. Note that this should not be done manually. Software is
// expected to get the key in two shares right from the beginning.

uint8_t key_share0_t[sizeof(kAesModesKey256)];
for (int i = 0; i < sizeof(kAesModesKey256); ++i) {
key_share0_t[i] = kAesModesKey256[i] ^ kKeyShare1[i];
}

// "Convert" key share byte arrays to `dif_aes_key_share_t`.
dif_aes_key_share_t key_t;
memcpy(key_t.share0, key_share0, sizeof(key_t.share0));
memcpy(key_t.share1, kKeyShare1, sizeof(key_t.share1));

dif_aes_iv_t iv2;
memcpy(iv2.iv, kAesModesIvCtr, sizeof(iv2.iv));

dif_aes_transaction_t transaction_trial = {
.operation = kDifAesOperationEncrypt,
.mode = kDifAesModeCtr,
.key_len = kDifAesKey256,
.key_provider = kDifAesKeySoftwareProvided,
.mask_reseeding = kDifAesReseedPerBlock,
.manual_operation = kDifAesManualOperationAuto,
.reseed_on_key_change = false,
.ctrl_aux_lock = false,
};

TRY(dif_aes_start(&aes, &transaction_trial, &key_t, &iv2));

// "Convert" plain data byte arrays to `dif_aes_data_t` array.
enum {
kAesNumBlocks_t2 = 4,
};

dif_aes_data_t plain_text2[kAesNumBlocks_t2];
memcpy(plain_text2[0].data, kAesModesPlainText, sizeof(kAesModesPlainText));
TRY(dif_aes_load_data(&aes, plain_text2[0]));

do {
TRY(dif_aes_get_status(&aes, kDifAesStatusOutputValid, &ready));
} while (!ready);

//*****************Trial encryption ends ******************
// Not checking successful encryption here since its a trial transaction
// Trial encryption ends here
// Resume the Original Encryption below

// Using the iv read from `iv_encrypt1` for resuming the encryption of
// remaining 2 blocks.

memcpy(iv.iv, &iv_encrypt1, sizeof(iv.iv));
transaction.manual_operation = kDifAesManualOperationAuto;

// "Convert" key share byte arrays to `dif_aes_key_share_t`.
memcpy(key.share0, key_share0, sizeof(key.share0));
memcpy(key.share1, kKeyShare1, sizeof(key.share1));

TRY(dif_aes_start(&aes, &transaction, &key, &iv));

memcpy(plain_text_block34[0].data, kAesModesPlainTextBlock34,
sizeof(kAesModesPlainTextBlock34));

TRY(dif_aes_get_status(&aes, kDifAesStatusInputReady, &ready));
LOG_INFO("Resuming Encrypt with the remaining 2 blocks of data");
TRY(dif_aes_process_data(&aes, plain_text_block34, cipher_text_block34,
(size_t)kAesNumBlocks));

// Decrypt operation below
dif_aes_transaction_t transactiond = {
.operation = kDifAesOperationDecrypt,
.mode = aes_mode,
.key_len = kDifAesKey128,
.key_provider = kDifAesKeySoftwareProvided,
.mask_reseeding = kDifAesReseedPerBlock,
.manual_operation = kDifAesManualOperationAuto,
.reseed_on_key_change = false,
.ctrl_aux_lock = false,
};

do {
TRY(dif_aes_get_status(&aes, kDifAesStatusInputReady, &ready));
} while (!ready);

TRY(dif_aes_start(&aes, &transactiond, &key, &iv_mode));
// Finish the decryption transaction.
dif_aes_data_t out_data2[4];
for (int i = 0; i < 2; ++i) {
TRY(dif_aes_load_data(&aes, cipher_text[i]));

do {
TRY(dif_aes_get_status(&aes, kDifAesStatusOutputValid, &ready));
} while (!ready);

TRY(dif_aes_read_output(&aes, &out_data2[i]));
}
for (int i = 2; i < 4; ++i) {
TRY(dif_aes_load_data(&aes, cipher_text_block34[i - 2]));

do {
TRY(dif_aes_get_status(&aes, kDifAesStatusOutputValid, &ready));
} while (!ready);

TRY(dif_aes_read_output(&aes, &out_data2[i]));
}

TRY(dif_aes_end(&aes));
CHECK_ARRAYS_EQ((uint8_t *)out_data2, kAesModesPlainText,
sizeof(kAesModesPlainText));
LOG_INFO("Decryption Successful");
}
return OK_STATUS();
}

bool test_main(void) {
LOG_INFO("Entering AES Interrupt Encrypt Test");
return status_ok(execute_test());
}

0 comments on commit 26591f0

Please sign in to comment.