Skip to content

[crypto] Wipe OTBN's DMEM in failure cases #27650

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions sw/device/lib/crypto/drivers/otbn.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,27 @@
extern "C" {
#endif

/**
* Hardened version of the `TRY` macro that wipes DMEM() on an error.
*
* Returns an error if either expr_ represents an error, or if the OK code does
* not match the expected hardened value.
*
* @param expr_ An expression that evaluates to a `status_t`.
* @return The enclosed OK value.
*/
#define HARDENED_TRY_WIPE_DMEM(expr_) \
do { \
status_t status_ = expr_; \
if (launder32(OT_UNSIGNED(status_.value)) != kHardenedBoolTrue) { \
otbn_dmem_sec_wipe(); \
return (status_t){ \
.value = (int32_t)(OT_UNSIGNED(status_.value) | 0x80000000)}; \
} \
HARDENED_CHECK_EQ(status_.value, kHardenedBoolTrue); \
status_.value; \
} while (false)

/**
* Constants related to OTBN wide words
*/
Expand Down
50 changes: 30 additions & 20 deletions sw/device/lib/crypto/impl/ecc/p256.c
Original file line number Diff line number Diff line change
Expand Up @@ -123,29 +123,33 @@ status_t p256_sideload_keygen_start(void) {
status_t p256_keygen_finalize(p256_masked_scalar_t *private_key,
p256_point_t *public_key) {
// Spin here waiting for OTBN to complete.
HARDENED_TRY(otbn_busy_wait_for_done());
HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done());

// Read the masked private key from OTBN dmem.
HARDENED_TRY(otbn_dmem_read(kP256MaskedScalarShareWords, kOtbnVarD0,
private_key->share0));
HARDENED_TRY(otbn_dmem_read(kP256MaskedScalarShareWords, kOtbnVarD1,
private_key->share1));
HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(kP256MaskedScalarShareWords, kOtbnVarD0,
private_key->share0));
HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(kP256MaskedScalarShareWords, kOtbnVarD1,
private_key->share1));

// Read the public key from OTBN dmem.
HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarX, public_key->x));
HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarY, public_key->y));
HARDENED_TRY_WIPE_DMEM(
otbn_dmem_read(kP256CoordWords, kOtbnVarX, public_key->x));
HARDENED_TRY_WIPE_DMEM(
otbn_dmem_read(kP256CoordWords, kOtbnVarY, public_key->y));

// Wipe DMEM.
return otbn_dmem_sec_wipe();
}

status_t p256_sideload_keygen_finalize(p256_point_t *public_key) {
// Spin here waiting for OTBN to complete.
HARDENED_TRY(otbn_busy_wait_for_done());
HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done());

// Read the public key from OTBN dmem.
HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarX, public_key->x));
HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarY, public_key->y));
HARDENED_TRY_WIPE_DMEM(
otbn_dmem_read(kP256CoordWords, kOtbnVarX, public_key->x));
HARDENED_TRY_WIPE_DMEM(
otbn_dmem_read(kP256CoordWords, kOtbnVarY, public_key->y));

// Wipe DMEM.
return otbn_dmem_sec_wipe();
Expand Down Expand Up @@ -212,13 +216,15 @@ status_t p256_ecdsa_sideload_sign_start(

status_t p256_ecdsa_sign_finalize(p256_ecdsa_signature_t *result) {
// Spin here waiting for OTBN to complete.
HARDENED_TRY(otbn_busy_wait_for_done());
HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done());

// Read signature R out of OTBN dmem.
HARDENED_TRY(otbn_dmem_read(kP256ScalarWords, kOtbnVarR, result->r));
HARDENED_TRY_WIPE_DMEM(
otbn_dmem_read(kP256ScalarWords, kOtbnVarR, result->r));

// Read signature S out of OTBN dmem.
HARDENED_TRY(otbn_dmem_read(kP256ScalarWords, kOtbnVarS, result->s));
HARDENED_TRY_WIPE_DMEM(
otbn_dmem_read(kP256ScalarWords, kOtbnVarS, result->s));

// Wipe DMEM.
return otbn_dmem_sec_wipe();
Expand Down Expand Up @@ -256,20 +262,21 @@ status_t p256_ecdsa_verify_start(const p256_ecdsa_signature_t *signature,
status_t p256_ecdsa_verify_finalize(const p256_ecdsa_signature_t *signature,
hardened_bool_t *result) {
// Spin here waiting for OTBN to complete.
HARDENED_TRY(otbn_busy_wait_for_done());
HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done());

// Read the status code out of DMEM (false if basic checks on the validity of
// the signature and public key failed).
uint32_t ok;
HARDENED_TRY(otbn_dmem_read(1, kOtbnVarOk, &ok));
HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(1, kOtbnVarOk, &ok));
if (launder32(ok) != kHardenedBoolTrue) {
HARDENED_TRY(otbn_dmem_sec_wipe());
return OTCRYPTO_BAD_ARGS;
}
HARDENED_CHECK_EQ(ok, kHardenedBoolTrue);

// Read x_r (recovered R) out of OTBN dmem.
uint32_t x_r[kP256ScalarWords];
HARDENED_TRY(otbn_dmem_read(kP256ScalarWords, kOtbnVarXr, x_r));
HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(kP256ScalarWords, kOtbnVarXr, x_r));

*result = hardened_memeq(x_r, signature->r, kP256ScalarWords);

Expand Down Expand Up @@ -301,19 +308,22 @@ status_t p256_ecdh_start(const p256_masked_scalar_t *private_key,

status_t p256_ecdh_finalize(p256_ecdh_shared_key_t *shared_key) {
// Spin here waiting for OTBN to complete.
HARDENED_TRY(otbn_busy_wait_for_done());
HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done());

// Read the code indicating if the public key is valid.
uint32_t ok;
HARDENED_TRY(otbn_dmem_read(1, kOtbnVarOk, &ok));
HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(1, kOtbnVarOk, &ok));
if (launder32(ok) != kHardenedBoolTrue) {
HARDENED_TRY(otbn_dmem_sec_wipe());
return OTCRYPTO_BAD_ARGS;
}
HARDENED_CHECK_EQ(ok, kHardenedBoolTrue);

// Read the shares of the key from OTBN dmem (at vars x and y).
HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarX, shared_key->share0));
HARDENED_TRY(otbn_dmem_read(kP256CoordWords, kOtbnVarY, shared_key->share1));
HARDENED_TRY_WIPE_DMEM(
otbn_dmem_read(kP256CoordWords, kOtbnVarX, shared_key->share0));
HARDENED_TRY_WIPE_DMEM(
otbn_dmem_read(kP256CoordWords, kOtbnVarY, shared_key->share1));

// Wipe DMEM.
return otbn_dmem_sec_wipe();
Expand Down
50 changes: 30 additions & 20 deletions sw/device/lib/crypto/impl/ecc/p384.c
Original file line number Diff line number Diff line change
Expand Up @@ -164,17 +164,19 @@ status_t p384_keygen_start(void) {
status_t p384_keygen_finalize(p384_masked_scalar_t *private_key,
p384_point_t *public_key) {
// Spin here waiting for OTBN to complete.
HARDENED_TRY(otbn_busy_wait_for_done());
HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done());

// Read the masked private key from OTBN dmem.
HARDENED_TRY(otbn_dmem_read(kP384MaskedScalarShareWords, kOtbnVarD0,
private_key->share0));
HARDENED_TRY(otbn_dmem_read(kP384MaskedScalarShareWords, kOtbnVarD1,
private_key->share1));
HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(kP384MaskedScalarShareWords, kOtbnVarD0,
private_key->share0));
HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(kP384MaskedScalarShareWords, kOtbnVarD1,
private_key->share1));

// Read the public key from OTBN dmem.
HARDENED_TRY(otbn_dmem_read(kP384CoordWords, kOtbnVarX, public_key->x));
HARDENED_TRY(otbn_dmem_read(kP384CoordWords, kOtbnVarY, public_key->y));
HARDENED_TRY_WIPE_DMEM(
otbn_dmem_read(kP384CoordWords, kOtbnVarX, public_key->x));
HARDENED_TRY_WIPE_DMEM(
otbn_dmem_read(kP384CoordWords, kOtbnVarY, public_key->y));

// Wipe DMEM.
return otbn_dmem_sec_wipe();
Expand All @@ -194,11 +196,13 @@ status_t p384_sideload_keygen_start(void) {

status_t p384_sideload_keygen_finalize(p384_point_t *public_key) {
// Spin here waiting for OTBN to complete.
HARDENED_TRY(otbn_busy_wait_for_done());
HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done());

// Read the public key from OTBN dmem.
HARDENED_TRY(otbn_dmem_read(kP384CoordWords, kOtbnVarX, public_key->x));
HARDENED_TRY(otbn_dmem_read(kP384CoordWords, kOtbnVarY, public_key->y));
HARDENED_TRY_WIPE_DMEM(
otbn_dmem_read(kP384CoordWords, kOtbnVarX, public_key->x));
HARDENED_TRY_WIPE_DMEM(
otbn_dmem_read(kP384CoordWords, kOtbnVarY, public_key->y));

// Wipe DMEM.
return otbn_dmem_sec_wipe();
Expand Down Expand Up @@ -241,13 +245,15 @@ status_t p384_ecdsa_sideload_sign_start(

status_t p384_ecdsa_sign_finalize(p384_ecdsa_signature_t *result) {
// Spin here waiting for OTBN to complete.
HARDENED_TRY(otbn_busy_wait_for_done());
HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done());

// Read signature R out of OTBN dmem.
HARDENED_TRY(otbn_dmem_read(kP384ScalarWords, kOtbnVarR, result->r));
HARDENED_TRY_WIPE_DMEM(
otbn_dmem_read(kP384ScalarWords, kOtbnVarR, result->r));

// Read signature S out of OTBN dmem.
HARDENED_TRY(otbn_dmem_read(kP384ScalarWords, kOtbnVarS, result->s));
HARDENED_TRY_WIPE_DMEM(
otbn_dmem_read(kP384ScalarWords, kOtbnVarS, result->s));

// Wipe DMEM.
return otbn_dmem_sec_wipe();
Expand Down Expand Up @@ -282,20 +288,21 @@ status_t p384_ecdsa_verify_start(const p384_ecdsa_signature_t *signature,
status_t p384_ecdsa_verify_finalize(const p384_ecdsa_signature_t *signature,
hardened_bool_t *result) {
// Spin here waiting for OTBN to complete.
HARDENED_TRY(otbn_busy_wait_for_done());
HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done());

// Read the status code out of DMEM (false if basic checks on the validity of
// the signature and public key failed).
uint32_t ok;
HARDENED_TRY(otbn_dmem_read(1, kOtbnVarOk, &ok));
HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(1, kOtbnVarOk, &ok));
if (launder32(ok) != kHardenedBoolTrue) {
HARDENED_TRY(otbn_dmem_sec_wipe());
return OTCRYPTO_BAD_ARGS;
}
HARDENED_CHECK_EQ(ok, kHardenedBoolTrue);

// Read x_r (recovered R) out of OTBN dmem.
uint32_t x_r[kP384ScalarWords];
HARDENED_TRY(otbn_dmem_read(kP384ScalarWords, kOtbnVarXr, x_r));
HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(kP384ScalarWords, kOtbnVarXr, x_r));

*result = hardened_memeq(x_r, signature->r, kP384ScalarWords);

Expand Down Expand Up @@ -324,20 +331,23 @@ status_t p384_ecdh_start(const p384_masked_scalar_t *private_key,

status_t p384_ecdh_finalize(p384_ecdh_shared_key_t *shared_key) {
// Spin here waiting for OTBN to complete.
HARDENED_TRY(otbn_busy_wait_for_done());
HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done());

// Read the status code out of DMEM (false if basic checks on the validity of
// the signature and public key failed).
uint32_t ok;
HARDENED_TRY(otbn_dmem_read(1, kOtbnVarOk, &ok));
HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(1, kOtbnVarOk, &ok));
if (launder32(ok) != kHardenedBoolTrue) {
HARDENED_TRY(otbn_dmem_sec_wipe());
return OTCRYPTO_BAD_ARGS;
}
HARDENED_CHECK_EQ(ok, kHardenedBoolTrue);

// Read the shares of the key from OTBN dmem (at vars x and y).
HARDENED_TRY(otbn_dmem_read(kP384CoordWords, kOtbnVarX, shared_key->share0));
HARDENED_TRY(otbn_dmem_read(kP384CoordWords, kOtbnVarY, shared_key->share1));
HARDENED_TRY_WIPE_DMEM(
otbn_dmem_read(kP384CoordWords, kOtbnVarX, shared_key->share0));
HARDENED_TRY_WIPE_DMEM(
otbn_dmem_read(kP384CoordWords, kOtbnVarY, shared_key->share1));

// Wipe DMEM.
return otbn_dmem_sec_wipe();
Expand Down
17 changes: 10 additions & 7 deletions sw/device/lib/crypto/impl/rsa/rsa_3072_verify.c
Original file line number Diff line number Diff line change
Expand Up @@ -137,16 +137,18 @@ status_t rsa_3072_compute_constants(const rsa_3072_public_key_t *public_key,
HARDENED_TRY(otbn_execute());

// Spin here waiting for OTBN to complete.
HARDENED_TRY(otbn_busy_wait_for_done());
HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done());

// Read constant rr out of DMEM.
HARDENED_TRY(read_rsa_3072_int_from_otbn(kOtbnVarRsaRR, &result->rr));
HARDENED_TRY_WIPE_DMEM(
read_rsa_3072_int_from_otbn(kOtbnVarRsaRR, &result->rr));

// Read constant m0_inv out of DMEM.
HARDENED_TRY(
HARDENED_TRY_WIPE_DMEM(
otbn_dmem_read(kOtbnWideWordNumWords, kOtbnVarRsaM0Inv, result->m0_inv));

return OTCRYPTO_OK;
// Wipe DMEM.
return otbn_dmem_sec_wipe();
}

status_t rsa_3072_verify_start(const rsa_3072_int_t *signature,
Expand Down Expand Up @@ -196,11 +198,11 @@ status_t rsa_3072_verify_finalize(const rsa_3072_int_t *message,
*result = kHardenedBoolFalse;

// Spin here waiting for OTBN to complete.
HARDENED_TRY(otbn_busy_wait_for_done());
HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done());

// Read recovered message out of OTBN dmem.
rsa_3072_int_t recoveredMessage;
HARDENED_TRY(
HARDENED_TRY_WIPE_DMEM(
read_rsa_3072_int_from_otbn(kOtbnVarRsaOutBuf, &recoveredMessage));

// TODO: harden this memory comparison
Expand All @@ -212,7 +214,8 @@ status_t rsa_3072_verify_finalize(const rsa_3072_int_t *message,
}
}

return OTCRYPTO_OK;
// Wipe DMEM.
return otbn_dmem_sec_wipe();
}

status_t rsa_3072_verify(const rsa_3072_int_t *signature,
Expand Down
13 changes: 6 additions & 7 deletions sw/device/lib/crypto/impl/rsa/rsa_keygen.c
Original file line number Diff line number Diff line change
Expand Up @@ -83,25 +83,24 @@ static status_t keygen_start(uint32_t mode) {
static status_t keygen_finalize(uint32_t exp_mode, size_t num_words,
uint32_t *n, uint32_t *d) {
// Spin here waiting for OTBN to complete.
HARDENED_TRY(otbn_busy_wait_for_done());
HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done());

// Read the mode from OTBN dmem and panic if it's not as expected.
uint32_t act_mode = 0;
HARDENED_TRY(otbn_dmem_read(1, kOtbnVarRsaMode, &act_mode));
HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(1, kOtbnVarRsaMode, &act_mode));
if (act_mode != exp_mode) {
HARDENED_TRY(otbn_dmem_sec_wipe());
return OTCRYPTO_FATAL_ERR;
}

// Read the public modulus (n) from OTBN dmem.
HARDENED_TRY(otbn_dmem_read(num_words, kOtbnVarRsaN, n));
HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(num_words, kOtbnVarRsaN, n));

// Read the private exponent (d) from OTBN dmem.
HARDENED_TRY(otbn_dmem_read(num_words, kOtbnVarRsaD, d));
HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(num_words, kOtbnVarRsaD, d));

// Wipe DMEM.
HARDENED_TRY(otbn_dmem_sec_wipe());

return OTCRYPTO_OK;
return otbn_dmem_sec_wipe();
}

status_t rsa_keygen_2048_start(void) {
Expand Down
12 changes: 8 additions & 4 deletions sw/device/lib/crypto/impl/rsa/rsa_modexp.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ enum {

status_t rsa_modexp_wait(size_t *num_words) {
// Spin here waiting for OTBN to complete.
HARDENED_TRY(otbn_busy_wait_for_done());
HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done());

// Read the application mode.
uint32_t mode;
HARDENED_TRY(otbn_dmem_read(1, kOtbnVarRsaMode, &mode));
HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(1, kOtbnVarRsaMode, &mode));

*num_words = 0;
if (mode == kMode2048Modexp || mode == kMode2048ModexpF4) {
Expand All @@ -72,6 +72,8 @@ status_t rsa_modexp_wait(size_t *num_words) {
} else if (mode == kMode4096Modexp || mode == kMode4096ModexpF4) {
*num_words = kRsa4096NumWords;
} else {
// Wipe DMEM.
HARDENED_TRY(otbn_dmem_sec_wipe());
// Unrecognized mode.
return OTCRYPTO_FATAL_ERR;
}
Expand All @@ -92,15 +94,17 @@ status_t rsa_modexp_wait(size_t *num_words) {
static status_t rsa_modexp_finalize(const size_t num_words, uint32_t *result) {
// Wait for OTBN to complete and get the result size.
size_t num_words_inferred;
HARDENED_TRY(rsa_modexp_wait(&num_words_inferred));
HARDENED_TRY_WIPE_DMEM(rsa_modexp_wait(&num_words_inferred));

// Check that the inferred result size matches expectations.
if (num_words != num_words_inferred) {
// Wipe DMEM.
HARDENED_TRY(otbn_dmem_sec_wipe());
return OTCRYPTO_FATAL_ERR;
}

// Read the result.
HARDENED_TRY(otbn_dmem_read(num_words, kOtbnVarRsaInOut, result));
HARDENED_TRY_WIPE_DMEM(otbn_dmem_read(num_words, kOtbnVarRsaInOut, result));

// Wipe DMEM.
return otbn_dmem_sec_wipe();
Expand Down
4 changes: 2 additions & 2 deletions sw/device/lib/crypto/impl/sha2/sha256.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ static status_t process_message_buffer(sha256_otbn_ctx_t *ctx) {

// Run the OTBN program.
HARDENED_TRY(otbn_execute());
HARDENED_TRY(otbn_busy_wait_for_done());
HARDENED_TRY_WIPE_DMEM(otbn_busy_wait_for_done());

// Reset the message buffer counter.
ctx->num_blocks = 0;
Expand Down Expand Up @@ -245,7 +245,7 @@ static status_t process_message(sha256_state_t *state, const uint8_t *msg,
}

// Read the final state from OTBN dmem.
HARDENED_TRY(
HARDENED_TRY_WIPE_DMEM(
otbn_dmem_read(kSha256StateWords, kOtbnVarSha256State, new_state.H));

// Clear OTBN's memory.
Expand Down
Loading
Loading