diff --git a/sw/device/lib/crypto/impl/ecc/curve25519.c b/sw/device/lib/crypto/impl/ecc/curve25519.c index 516b41cb5c7bc..40a973bb992c8 100644 --- a/sw/device/lib/crypto/impl/ecc/curve25519.c +++ b/sw/device/lib/crypto/impl/ecc/curve25519.c @@ -84,6 +84,8 @@ OTBN_DECLARE_SYMBOL_ADDR(run_curve25519, MODE_SIGN_STAGE2); OTBN_DECLARE_SYMBOL_ADDR(run_curve25519, MODE_VERIFY); OTBN_DECLARE_SYMBOL_ADDR(run_curve25519, MODE_X25519); OTBN_DECLARE_SYMBOL_ADDR(run_curve25519, MODE_X25519_KEYGEN); +OTBN_DECLARE_SYMBOL_ADDR(run_curve25519, MODE_X25519_SIDELOAD); +OTBN_DECLARE_SYMBOL_ADDR(run_curve25519, MODE_X25519_KEYGEN_SIDELOAD); static const uint32_t kOtbnCurve25519ModeKeygen = OTBN_ADDR_T_INIT(run_curve25519, MODE_KEYGEN); @@ -97,6 +99,10 @@ static const uint32_t kOtbnCurve25519ModeX25519 = OTBN_ADDR_T_INIT(run_curve25519, MODE_X25519); static const uint32_t kOtbnCurve25519ModeX25519Keygen = OTBN_ADDR_T_INIT(run_curve25519, MODE_X25519_KEYGEN); +static const uint32_t kOtbnCurve25519ModeX25519Sideload = + OTBN_ADDR_T_INIT(run_curve25519, MODE_X25519_SIDELOAD); +static const uint32_t kOtbnCurve25519ModeX25519KeygenSideload = + OTBN_ADDR_T_INIT(run_curve25519, MODE_X25519_KEYGEN_SIDELOAD); enum { /* @@ -318,6 +324,33 @@ status_t curve25519_x25519_start( return otbn_execute(); } +status_t curve25519_x25519_keygen_sideload_start(void) { + // Load the Curve25519 app. + HARDENED_TRY(otbn_load_app(kOtbnAppCurve25519)); + + // Set mode to jump into the hardware sideload keygen path. + uint32_t mode = kOtbnCurve25519ModeX25519KeygenSideload; + HARDENED_TRY(otbn_dmem_write(kCurve25519ModeWords, &mode, kOtbnVarMode)); + + // Start the OTBN routine. + return otbn_execute(); +} + +status_t curve25519_x25519_sideload_start( + const uint32_t public_key[kCurve25519PointWords]) { + // Load the Curve25519 app. Fails if OTBN is non-idle. + HARDENED_TRY(otbn_load_app(kOtbnAppCurve25519)); + + uint32_t mode = kOtbnCurve25519ModeX25519Sideload; + HARDENED_TRY(otbn_dmem_write(kCurve25519ModeWords, &mode, kOtbnVarMode)); + + HARDENED_TRY(otbn_dmem_write(kCurve25519PointWords, public_key, + kOtbnVarX25519PublicKey)); + + // Start the OTBN routine. + return otbn_execute(); +} + status_t curve25519_x25519_finalize( uint32_t shared_secret[kCurve25519PointWords]) { // Spin here waiting for OTBN to complete. diff --git a/sw/device/lib/crypto/impl/ecc/curve25519.h b/sw/device/lib/crypto/impl/ecc/curve25519.h index ffd1452aa912d..b65afa1211690 100644 --- a/sw/device/lib/crypto/impl/ecc/curve25519.h +++ b/sw/device/lib/crypto/impl/ecc/curve25519.h @@ -286,6 +286,27 @@ status_t curve25519_x25519_start( const uint32_t private_key[kCurve25519ScalarWords], const uint32_t public_key[kCurve25519PointWords]); +/** + * Start the X25519 keygen operation on OTBN using a sideloaded hardware key. + * + * @return Result of the operation. + */ +status_t curve25519_x25519_keygen_sideload_start(void); + +/** + * Start the X25519 operation on OTBN using a sideloaded hardware key. + * + * This routine writes the public key and mode to OTBN. It assumes the + * Key Manager has already programmed the private key into OTBN's + * sideload registers (KEY_S0_L and KEY_S1_L). + * + * @param public_key Public key u-coordinate. + * @return Result of the operation. + */ +OT_WARN_UNUSED_RESULT +status_t curve25519_x25519_sideload_start( + const uint32_t public_key[kCurve25519PointWords]); + /** * Finish an async X25519 key exchange operation on OTBN. * diff --git a/sw/device/lib/crypto/impl/ecc_curve25519.c b/sw/device/lib/crypto/impl/ecc_curve25519.c index 640d1f9d68b9d..21085ebcb5ee1 100644 --- a/sw/device/lib/crypto/impl/ecc_curve25519.c +++ b/sw/device/lib/crypto/impl/ecc_curve25519.c @@ -702,22 +702,33 @@ otcrypto_status_t otcrypto_x25519_keygen_async_start( HARDENED_TRY( curve25519_private_key_length_check(private_key, kOtcryptoKeyModeX25519)); - uint32_t private_key_unmasked[kCurve25519ScalarWords]; - uint32_t *share0 = private_key->keyblob; - uint32_t *share1 = - private_key->keyblob + keyblob_share_num_words(private_key->config); - HARDENED_TRY(hardened_add(share0, share1, kCurve25519ScalarWords, - private_key_unmasked)); + if (private_key->config.hw_backed == kHardenedBoolTrue) { + HARDENED_CHECK_EQ(launder32(private_key->config.hw_backed), + kHardenedBoolTrue); + + HARDENED_TRY(keyblob_sideload_key_otbn(private_key)); + + return otcrypto_eval_exit(curve25519_x25519_keygen_sideload_start()); + } else if (private_key->config.hw_backed == kHardenedBoolFalse) { + uint32_t private_key_unmasked[kCurve25519ScalarWords]; + uint32_t *share0 = private_key->keyblob; + uint32_t *share1 = + private_key->keyblob + keyblob_share_num_words(private_key->config); - HARDENED_TRY(ed25519_clamp(private_key_unmasked)); + HARDENED_TRY(hardened_add(share0, share1, kCurve25519ScalarWords, + private_key_unmasked)); - // Start the OTBN key exchange app. - HARDENED_TRY(curve25519_x25519_keygen_start(private_key_unmasked)); + HARDENED_TRY(ed25519_clamp(private_key_unmasked)); - // Wipe the unmasked private key. - HARDENED_TRY(hardened_memshred(private_key_unmasked, kCurve25519ScalarWords)); + // Start the OTBN key exchange app. + HARDENED_TRY(curve25519_x25519_keygen_start(private_key_unmasked)); - return otcrypto_eval_exit(OTCRYPTO_OK); + // Wipe the unmasked private key. + return otcrypto_eval_exit( + hardened_memshred(private_key_unmasked, kCurve25519ScalarWords)); + } + + return OTCRYPTO_BAD_ARGS; } otcrypto_status_t otcrypto_x25519_keygen_async_finalize( @@ -737,23 +748,36 @@ otcrypto_status_t otcrypto_x25519_async_start( HARDENED_TRY( curve25519_public_key_length_check(public_key, kOtcryptoKeyModeX25519)); - uint32_t private_key_unmasked[kCurve25519ScalarWords]; - // Unmask the private key. - uint32_t *share0 = private_key->keyblob; - uint32_t *share1 = - private_key->keyblob + keyblob_share_num_words(private_key->config); - HARDENED_TRY(hardened_add(share0, share1, kCurve25519ScalarWords, - private_key_unmasked)); + if (private_key->config.hw_backed == kHardenedBoolTrue) { + HARDENED_CHECK_EQ(launder32(private_key->config.hw_backed), + kHardenedBoolTrue); - HARDENED_TRY(ed25519_clamp(private_key_unmasked)); + HARDENED_TRY(keyblob_sideload_key_otbn(private_key)); - // Start the OTBN key exchange app. - HARDENED_TRY(curve25519_x25519_start(private_key_unmasked, public_key->key)); + return otcrypto_eval_exit( + curve25519_x25519_sideload_start(public_key->key)); - // Wipe the unmasked private key. - HARDENED_TRY(hardened_memshred(private_key_unmasked, kCurve25519ScalarWords)); + } else if (private_key->config.hw_backed == kHardenedBoolFalse) { + uint32_t private_key_unmasked[kCurve25519ScalarWords]; - return otcrypto_eval_exit(OTCRYPTO_OK); + // Unmask the private key + uint32_t *share0 = private_key->keyblob; + uint32_t *share1 = + private_key->keyblob + keyblob_share_num_words(private_key->config); + HARDENED_TRY(hardened_add(share0, share1, kCurve25519ScalarWords, + private_key_unmasked)); + HARDENED_TRY(ed25519_clamp(private_key_unmasked)); + + // Start the standard OTBN key exchange app + HARDENED_TRY( + curve25519_x25519_start(private_key_unmasked, public_key->key)); + + // Wipe the unmasked private key from CPU memory and return + return otcrypto_eval_exit( + hardened_memshred(private_key_unmasked, kCurve25519ScalarWords)); + } + + return OTCRYPTO_BAD_ARGS; } otcrypto_status_t otcrypto_x25519_async_finalize( diff --git a/sw/device/tests/BUILD b/sw/device/tests/BUILD index 5f58d803179cc..88482219e4a31 100644 --- a/sw/device/tests/BUILD +++ b/sw/device/tests/BUILD @@ -2289,7 +2289,7 @@ opentitan_test( "//sw/device/lib/testing/test_framework:ottf_alerts", "//sw/device/lib/testing/test_framework:ottf_main", "//sw/device/silicon_creator/lib/drivers:retention_sram", - "//sw/otbn/crypto:x25519_sideload", + "//sw/otbn/crypto:run_curve25519", ], ) @@ -2319,7 +2319,7 @@ opentitan_test( "//sw/device/lib/testing:sram_ctrl_testutils", "//sw/device/lib/testing/test_framework:ottf_main", "//sw/device/silicon_creator/lib/drivers:retention_sram", - "//sw/otbn/crypto:x25519_sideload", + "//sw/otbn/crypto:run_curve25519", ], ) @@ -2522,7 +2522,7 @@ opentitan_test( "//sw/device/lib/testing:otbn_testutils", "//sw/device/lib/testing/test_framework:ottf_alerts", "//sw/device/lib/testing/test_framework:ottf_main", - "//sw/otbn/crypto:x25519_sideload", + "//sw/otbn/crypto:run_curve25519", ], ) @@ -2553,7 +2553,7 @@ opentitan_test( "//sw/device/lib/testing:keymgr_testutils", "//sw/device/lib/testing:otbn_testutils", "//sw/device/lib/testing/test_framework:ottf_main", - "//sw/otbn/crypto:x25519_sideload", + "//sw/otbn/crypto:run_curve25519", ], ) diff --git a/sw/device/tests/crypto/BUILD b/sw/device/tests/crypto/BUILD index fa73c7f9e7f5f..757901f6d71ba 100644 --- a/sw/device/tests/crypto/BUILD +++ b/sw/device/tests/crypto/BUILD @@ -746,6 +746,33 @@ opentitan_test( ], ) +opentitan_test( + name = "x25519_sideload_functest", + srcs = ["x25519_sideload_functest.c"], + exec_env = CRYPTOTEST_EXEC_ENVS, + verilator = verilator_params( + timeout = "eternal", + # This test can take > 60 minutes, so mark it manual as it shouldn't + # run in CI/nightlies. + tags = ["manual"], + ), + deps = [ + "//sw/device/lib/base:hardened_memory", + "//sw/device/lib/crypto/drivers:otbn", + "//sw/device/lib/crypto/impl:config", + "//sw/device/lib/crypto/impl:ecc_curve25519", + "//sw/device/lib/crypto/impl:entropy_src", + "//sw/device/lib/crypto/impl:integrity", + "//sw/device/lib/crypto/impl:key_transport", + "//sw/device/lib/crypto/impl:keyblob", + "//sw/device/lib/crypto/impl:sha2", + "//sw/device/lib/runtime:log", + "//sw/device/lib/testing:entropy_testutils", + "//sw/device/lib/testing:keymgr_testutils", + "//sw/device/lib/testing/test_framework:ottf_main", + ], +) + opentitan_test( name = "entropy_src_functest", srcs = ["entropy_src_functest.c"], diff --git a/sw/device/tests/crypto/x25519_sideload_functest.c b/sw/device/tests/crypto/x25519_sideload_functest.c new file mode 100644 index 0000000000000..500ea55ce7121 --- /dev/null +++ b/sw/device/tests/crypto/x25519_sideload_functest.c @@ -0,0 +1,220 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#include "sw/device/lib/base/hardened_memory.h" +#include "sw/device/lib/crypto/drivers/otbn.h" +#include "sw/device/lib/crypto/impl/keyblob.h" +#include "sw/device/lib/crypto/include/config.h" +#include "sw/device/lib/crypto/include/ecc_curve25519.h" +#include "sw/device/lib/crypto/include/entropy_src.h" +#include "sw/device/lib/crypto/include/integrity.h" +#include "sw/device/lib/crypto/include/key_transport.h" +#include "sw/device/lib/runtime/log.h" +#include "sw/device/lib/testing/keymgr_testutils.h" +#include "sw/device/lib/testing/test_framework/check.h" +#include "sw/device/lib/testing/test_framework/ottf_main.h" + +// Module ID for status codes. +#define MODULE_ID MAKE_MODULE_ID('x', 's', 't') + +enum { + /** + * Number of words needed to encode a mode of operation. + */ + kCurve25519ModeWords = 1, + /** + * Number of words needed to encode the verification result. + */ + kCurve25519ResultWords = 1, + /** + * Number of words needed to hold an encoded point for Curve25519. + */ + kCurve25519PointWords = 8, + /** + * Number of bytes needed to hold an encoded point for Curve25519. + */ + kCurve25519PointBytes = kCurve25519PointWords * 4, + /** + * Number of bytes needed to hold a encoded public or private key. + */ + kCurve25519KeyBytes = 32, + /** + * Number of bytes needed to hold a scalar. + */ + kCurve25519ScalarBytes = 32, + /** + * Number of words needed to hold a scalar. + */ + kCurve25519ScalarWords = kCurve25519ScalarBytes / 4, + /** + * Length of a Curve25519 curve point coordinate in bits. + */ + kCurve25519CoordBits = 256, + /** + * Length of a Curve25519 curve point coordinate in bytes. + */ + kCurve25519CoordBytes = kCurve25519CoordBits / 8, + /** + * Length of a Curve25519 curve point coordinate in words. + */ + kCurve25519CoordWords = kCurve25519CoordBytes / sizeof(uint32_t), + /** + * Length of an element in the Curve25519 scalar field in bits. + */ + kCurve25519ScalarBits = 256, + /** + * Number of bytes in an ECDH/X25519 shared key (32 bytes). + */ + kX25519SharedKeyBytes = 256 / 8, + /** + * Number of 32-bit words in an ECDH/X25519 shared key (8 words). + */ + kX25519SharedKeyWords = kX25519SharedKeyBytes / sizeof(uint32_t), +}; + +// Versions for private keys A and B. +static const uint32_t kPrivateKeyAVersion = 0; +static const uint32_t kPrivateKeyBVersion = 0; + +// Salt for private keys A and B. +static const uint32_t kPrivateKeyASalt[7] = {0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef, 0xdeadbeef, 0xdeadbeef, + 0xdeadbeef}; +static const uint32_t kPrivateKeyBSalt[7] = {0xa0a1a2a3, 0xa4a5a6a7, 0xa8a9aaab, + 0xacadaeaf, 0xb0b1b2b3, 0xb4b5b6b7, + 0xb8b9babb}; + +// Configuration for the private key. +static const otcrypto_key_config_t kX25519PrivateKeyConfig = { + .version = kOtcryptoLibVersion1, + .key_mode = kOtcryptoKeyModeX25519, + .key_length = kCurve25519KeyBytes, + .hw_backed = kHardenedBoolTrue, + .security_level = kOtcryptoKeySecurityLevelLow, +}; + +// Configuration for the X25519 shared (symmetric) key. +static const otcrypto_key_config_t kX25519SharedKeyConfig = { + .version = kOtcryptoLibVersion1, + .key_mode = kOtcryptoKeyModeAesCtr, + .key_length = kCurve25519KeyBytes, + .hw_backed = kHardenedBoolFalse, + .security_level = kOtcryptoKeySecurityLevelLow, +}; + +status_t key_exchange_test(void) { + uint32_t keyblobA[keyblob_num_words(kX25519PrivateKeyConfig)]; + otcrypto_blinded_key_t private_keyA = { + .config = kX25519PrivateKeyConfig, + .keyblob_length = sizeof(keyblobA), + .keyblob = keyblobA, + }; + TRY(otcrypto_hw_backed_key(kPrivateKeyAVersion, kPrivateKeyASalt, + &private_keyA)); + + uint32_t keyblobB[keyblob_num_words(kX25519PrivateKeyConfig)]; + otcrypto_blinded_key_t private_keyB = { + .config = kX25519PrivateKeyConfig, + .keyblob_length = sizeof(keyblobB), + .keyblob = keyblobB, + }; + TRY(otcrypto_hw_backed_key(kPrivateKeyBVersion, kPrivateKeyBSalt, + &private_keyB)); + + uint32_t pkA[kCurve25519PointWords] = {0}; + uint32_t pkB[kCurve25519PointWords] = {0}; + otcrypto_unblinded_key_t public_keyA = { + .key_mode = kOtcryptoKeyModeX25519, + .key_length = sizeof(pkA), + .key = pkA, + }; + otcrypto_unblinded_key_t public_keyB = { + .key_mode = kOtcryptoKeyModeX25519, + .key_length = sizeof(pkB), + .key = pkB, + }; + + // Generate a keypair. + LOG_INFO("Generating X25519 keypair A"); + TRY(otcrypto_x25519_keygen(&private_keyA, &public_keyA)); + + // Generate a second keypair. + LOG_INFO("Generating X25519 keypair B"); + TRY(otcrypto_x25519_keygen(&private_keyB, &public_keyB)); + + // Public keys should be different from each other. + CHECK_ARRAYS_NE(pkA, pkB, ARRAYSIZE(pkA)); + + uint32_t shared_keyblobA[keyblob_num_words(kX25519SharedKeyConfig)]; + otcrypto_blinded_key_t shared_keyA = { + .config = kX25519SharedKeyConfig, + .keyblob_length = sizeof(shared_keyblobA), + .keyblob = shared_keyblobA, + .checksum = 0, + }; + uint32_t shared_keyblobB[keyblob_num_words(kX25519SharedKeyConfig)]; + otcrypto_blinded_key_t shared_keyB = { + .config = kX25519SharedKeyConfig, + .keyblob_length = sizeof(shared_keyblobB), + .keyblob = shared_keyblobB, + .checksum = 0, + }; + + // Compute the shared secret from A's side. + LOG_INFO("Generating shared secret (A)"); + TRY(otcrypto_x25519(&private_keyA, &public_keyB, &shared_keyA)); + + // Compute the shared secret from B's side. + LOG_INFO("Generating shared secret (B)"); + TRY(otcrypto_x25519(&private_keyB, &public_keyA, &shared_keyB)); + + // Unmask the keys and check that they match. + uint32_t *keyA0; + uint32_t *keyA1; + TRY(keyblob_to_shares(&shared_keyA, &keyA0, &keyA1)); + uint32_t *keyB0; + uint32_t *keyB1; + TRY(keyblob_to_shares(&shared_keyB, &keyB0, &keyB1)); + + uint32_t keyA[kX25519SharedKeyWords]; + uint32_t keyB[kX25519SharedKeyWords]; + + TRY(hardened_add(keyA0, keyA1, kX25519SharedKeyWords, keyA)); + TRY(hardened_add(keyB0, keyB1, kX25519SharedKeyWords, keyB)); + + CHECK_ARRAYS_EQ(keyA, keyB, ARRAYSIZE(keyA)); + + return OTCRYPTO_OK; +} + +static status_t test_setup(void) { + // Initialize the key manager and advance to OwnerRootKey state. + dif_keymgr_t keymgr; + dif_kmac_t kmac; + dif_keymgr_state_t keymgr_state; + TRY(keymgr_testutils_try_startup(&keymgr, &kmac, &keymgr_state)); + + if (keymgr_state == kDifKeymgrStateCreatorRootKey) { + TRY(keymgr_testutils_advance_state(&keymgr, &kOwnerIntParams)); + TRY(keymgr_testutils_advance_state(&keymgr, &kOwnerRootKeyParams)); + } else if (keymgr_state == kDifKeymgrStateOwnerIntermediateKey) { + TRY(keymgr_testutils_advance_state(&keymgr, &kOwnerRootKeyParams)); + } + + TRY(keymgr_testutils_check_state(&keymgr, kDifKeymgrStateOwnerRootKey)); + + // Initialize entropy complex for cryptolib. + return otcrypto_init(kOtcryptoKeySecurityLevelLow); +} + +OTTF_DEFINE_TEST_CONFIG(); + +bool test_main(void) { + status_t result = OK_STATUS(); + + CHECK_STATUS_OK(test_setup()); + EXECUTE_TEST(result, key_exchange_test); + + return status_ok(result); +} diff --git a/sw/device/tests/keymgr_derive_cdi_test.c b/sw/device/tests/keymgr_derive_cdi_test.c index a67d8384131a6..4c43edec4c295 100644 --- a/sw/device/tests/keymgr_derive_cdi_test.c +++ b/sw/device/tests/keymgr_derive_cdi_test.c @@ -63,14 +63,16 @@ typedef struct cdi_outputs { // Symbols of the OTBN X22519 public key generation program. // See sw/otbn/crypto/x25519_sideload.s for the source code. -OTBN_DECLARE_APP_SYMBOLS(x25519_sideload); -OTBN_DECLARE_SYMBOL_ADDR(x25519_sideload, enc_u); -OTBN_DECLARE_SYMBOL_ADDR(x25519_sideload, enc_result); -static const otbn_app_t kOtbnAppX25519 = OTBN_APP_T_INIT(x25519_sideload); -static const otbn_addr_t kOtbnVarEncU = - OTBN_ADDR_T_INIT(x25519_sideload, enc_u); -static const otbn_addr_t kOtbnVarEncResult = - OTBN_ADDR_T_INIT(x25519_sideload, enc_result); +OTBN_DECLARE_APP_SYMBOLS(run_curve25519); +OTBN_DECLARE_SYMBOL_ADDR(run_curve25519, mode); +OTBN_DECLARE_SYMBOL_ADDR(run_curve25519, MODE_X25519_KEYGEN_SIDELOAD); +OTBN_DECLARE_SYMBOL_ADDR(run_curve25519, x25519_public_key); +static const otbn_app_t kOtbnAppX25519 = OTBN_APP_T_INIT(run_curve25519); +static const otbn_addr_t kOtbnVarMode = OTBN_ADDR_T_INIT(run_curve25519, mode); +static const uint32_t kOtbnCurve25519ModeX25519KeygenSideload = + OTBN_ADDR_T_INIT(run_curve25519, MODE_X25519_KEYGEN_SIDELOAD); +static const otbn_addr_t kOtbnVarX25519PublicKey = + OTBN_ADDR_T_INIT(run_curve25519, x25519_public_key); OTTF_DEFINE_TEST_CONFIG(); @@ -224,18 +226,15 @@ static void derive_sideload_otbn_key(const char *state_name, CHECK_STATUS_OK(otbn_testutils_load_app(&otbn, kOtbnAppX25519)); CHECK_DIF_OK(dif_otbn_set_ctrl_software_errs_fatal(&otbn, false)); - const uint32_t kEncodedU[8] = { - // Montgomery u-Coordinate. - 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - }; - CHECK_STATUS_OK(otbn_testutils_write_data(&otbn, sizeof(kEncodedU), - &kEncodedU, kOtbnVarEncU)); + uint32_t mode = kOtbnCurve25519ModeX25519KeygenSideload; + CHECK_STATUS_OK( + otbn_testutils_write_data(&otbn, sizeof(mode), &mode, kOtbnVarMode)); LOG_INFO("Starting OTBN program..."); CHECK_DIF_OK(dif_otbn_set_ctrl_software_errs_fatal(&otbn, false)); CHECK_STATUS_OK(otbn_testutils_execute(&otbn)); CHECK_STATUS_OK(otbn_testutils_wait_for_done(&otbn, 0)); CHECK_STATUS_OK(otbn_testutils_read_data(&otbn, kX2551PublicKeySizeBytes, - kOtbnVarEncResult, key)); + kOtbnVarX25519PublicKey, key)); #ifndef DERIVE_ATTESTATION // If the key version is larger than the permitted maximum version, then diff --git a/sw/device/tests/keymgr_sideload_otbn_test.c b/sw/device/tests/keymgr_sideload_otbn_test.c index f08ef8d28bac2..47d4ac2202ca5 100644 --- a/sw/device/tests/keymgr_sideload_otbn_test.c +++ b/sw/device/tests/keymgr_sideload_otbn_test.c @@ -27,14 +27,16 @@ static dif_kmac_t kmac; static dif_otbn_t otbn; /* Set up pointers to symbols in the OTBN application. */ -OTBN_DECLARE_APP_SYMBOLS(x25519_sideload); -OTBN_DECLARE_SYMBOL_ADDR(x25519_sideload, enc_u); -OTBN_DECLARE_SYMBOL_ADDR(x25519_sideload, enc_result); -static const otbn_app_t kOtbnAppX25519 = OTBN_APP_T_INIT(x25519_sideload); -static const otbn_addr_t kOtbnVarEncU = - OTBN_ADDR_T_INIT(x25519_sideload, enc_u); -static const otbn_addr_t kOtbnVarEncResult = - OTBN_ADDR_T_INIT(x25519_sideload, enc_result); +OTBN_DECLARE_APP_SYMBOLS(run_curve25519); +OTBN_DECLARE_SYMBOL_ADDR(run_curve25519, mode); +OTBN_DECLARE_SYMBOL_ADDR(run_curve25519, MODE_X25519_KEYGEN_SIDELOAD); +OTBN_DECLARE_SYMBOL_ADDR(run_curve25519, x25519_public_key); +static const otbn_app_t kOtbnAppX25519 = OTBN_APP_T_INIT(run_curve25519); +static const otbn_addr_t kOtbnVarMode = OTBN_ADDR_T_INIT(run_curve25519, mode); +static const uint32_t kOtbnCurve25519ModeX25519KeygenSideload = + OTBN_ADDR_T_INIT(run_curve25519, MODE_X25519_KEYGEN_SIDELOAD); +static const otbn_addr_t kOtbnVarX25519PublicKey = + OTBN_ADDR_T_INIT(run_curve25519, x25519_public_key); OTTF_DEFINE_TEST_CONFIG(); @@ -50,16 +52,6 @@ static void init_peripheral_handles(void) { dif_otbn_init(mmio_region_from_addr(TOP_EARLGREY_OTBN_BASE_ADDR), &otbn)); } -/** - * Encoded Montgomery u-coordinate for testing. - * - * This value (9) is actually the u-coordinate of the Curve25519 base point, so - * the X25519 function will effectively compute the public key. This is the - * first step in key exchange (see RFC 7748, section 6.1). - */ -static const uint32_t kEncodedU[8] = { - 0x9, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, -}; static const dif_otbn_err_bits_t kOtbnInvalidKeyErr = 0x1 << OTBN_ERR_BITS_KEY_INVALID_BIT; static const dif_otbn_err_bits_t kErrBitsOk = 0x0; @@ -80,9 +72,9 @@ static void run_x25519_app(dif_otbn_t *otbn, uint32_t *result, dif_otbn_err_bits_t expect_err_bits) { CHECK_DIF_OK(dif_otbn_set_ctrl_software_errs_fatal(otbn, /*enable=*/false)); - // Copy the input argument (Montgomery u-coordinate). - CHECK_STATUS_OK(otbn_testutils_write_data(otbn, sizeof(kEncodedU), &kEncodedU, - kOtbnVarEncU)); + uint32_t mode = kOtbnCurve25519ModeX25519KeygenSideload; + CHECK_STATUS_OK( + otbn_testutils_write_data(otbn, sizeof(mode), &mode, kOtbnVarMode)); // Run the OTBN program and wait for it to complete. Clear software // error fatal flag as the test expects an intermediate error state. @@ -91,9 +83,8 @@ static void run_x25519_app(dif_otbn_t *otbn, uint32_t *result, CHECK_STATUS_OK(otbn_testutils_execute(otbn)); CHECK_STATUS_OK(otbn_testutils_wait_for_done(otbn, expect_err_bits)); - // Copy the result (also a 256-bit Montgomery u-coordinate). CHECK_STATUS_OK( - otbn_testutils_read_data(otbn, 32, kOtbnVarEncResult, result)); + otbn_testutils_read_data(otbn, 32, kOtbnVarX25519PublicKey, result)); } /** diff --git a/sw/otbn/crypto/25519_scalar.s b/sw/otbn/crypto/25519_scalar.s index c09871fde1c12..eddd8003d181b 100644 --- a/sw/otbn/crypto/25519_scalar.s +++ b/sw/otbn/crypto/25519_scalar.s @@ -7,6 +7,7 @@ .globl sc_reduce_768 .globl sc_mul .globl sc_blind +.globl x25519_clamp_shares /** * This library contains arithmetic for the scalar field of the Ed25519 @@ -375,6 +376,43 @@ sc_blind: ret +/** + * Clamps a Curve25519 scalar represented as two Boolean shares. + * + * To ensure k = w8 ^ w7 is clamped: + * - w8 (Share 0) gets bits 0-2 cleared, 255 cleared, 254 SET. + * - w7 (Share 1) gets bits 0-2 cleared, 255 cleared, 254 CLEARED. + * + * This routine runs in constant time. + * + * @param[in,out] w8: Share 0 of the scalar + * @param[in,out] w7: Share 1 of the scalar + * @param[in] w31: all-zero + * + * clobbered registers: w2, w7, w8 + * clobbered flag groups: none + */ +x25519_clamp_shares: + /* --- Clamp Share 0 (w8) --- */ + /* Clear bottom 3 bits */ + bn.rshi w8, w31, w8 >> 3 + /* Clear top 3 bits (shifts out original bits 253-255) */ + bn.rshi w8, w8, w31 >> 251 + /* Prepare bit 254 = 1 */ + bn.addi w2, w31, 1 + /* Shift right 2: inserts the 1 at bit 254, original bit 253 moves back to 253 */ + bn.rshi w8, w2, w8 >> 2 + + /* --- Clamp Share 1 (w7) --- */ + /* Clear bottom 3 bits */ + bn.rshi w7, w31, w7 >> 3 + /* Clear top 3 bits */ + bn.rshi w7, w7, w31 >> 251 + /* Prepare bit 254 = 0 (use w31 instead of w2) */ + bn.rshi w7, w31, w7 >> 2 + + ret + .data /* Modulus L = 2^252+27742317777372353535851937790883648493 */ diff --git a/sw/otbn/crypto/BUILD b/sw/otbn/crypto/BUILD index 1071f61654541..0beb5d79dc91a 100644 --- a/sw/otbn/crypto/BUILD +++ b/sw/otbn/crypto/BUILD @@ -468,17 +468,6 @@ otbn_library( ], ) -otbn_binary( - name = "x25519_sideload", - srcs = [ - "x25519_sideload.s", - ], - deps = [ - ":field25519", - ":x25519", - ], -) - CRYPTOLIB_OTBN_APPS = [ "run_curve25519", "run_p256", diff --git a/sw/otbn/crypto/run_curve25519.s b/sw/otbn/crypto/run_curve25519.s index b4e9d2afedec8..4e553782dba9b 100644 --- a/sw/otbn/crypto/run_curve25519.s +++ b/sw/otbn/crypto/run_curve25519.s @@ -12,7 +12,7 @@ /** * Mode magic values, generated with - * $ ./util/design/sparse-fsm-encode.py -d 6 -m 3 -n 11 \ + * $ ./util/design/sparse-fsm-encode.py -d 6 -m 8 -n 11 \ * --avoid-zero -s 380925547 * * Call the same utility with the same arguments and a higher -m to generate @@ -23,12 +23,14 @@ * as `li`. If support is added, we could use 32-bit values here instead of * 11-bit. */ -.equ MODE_KEYGEN, 0x6CE -.equ MODE_SIGN_STAGE1, 0x77D -.equ MODE_SIGN_STAGE2, 0x397 -.equ MODE_VERIFY, 0x5F2 -.equ MODE_X25519, 0x4B1 -.equ MODE_X25519_KEYGEN, 0x5A9 +.equ MODE_KEYGEN, 0x1F8 +.equ MODE_SIGN_STAGE1, 0x669 +.equ MODE_SIGN_STAGE2, 0x23E +.equ MODE_VERIFY, 0x54E +.equ MODE_X25519, 0x695 +.equ MODE_X25519_KEYGEN, 0x7A2 +.equ MODE_X25519_SIDELOAD, 0xE7 +.equ MODE_X25519_KEYGEN_SIDELOAD, 0x353 /** * Make the mode constants visible to Ibex. @@ -39,6 +41,8 @@ .globl MODE_VERIFY .globl MODE_X25519 .globl MODE_X25519_KEYGEN +.globl MODE_X25519_SIDELOAD +.globl MODE_X25519_KEYGEN_SIDELOAD .section .text.start .globl start @@ -65,6 +69,12 @@ start: addi x3, x0, MODE_X25519_KEYGEN beq x2, x3, x25519_keygen + addi x3, x0, MODE_X25519_SIDELOAD + beq x2, x3, x25519_sideload + + addi x3, x0, MODE_X25519_KEYGEN_SIDELOAD + beq x2, x3, x25519_keygen_sideload + /* Invalid mode; fail. */ unimp unimp @@ -209,6 +219,60 @@ x25519_keygen: ecall +x25519_sideload: + /* Zeroize w31 */ + bn.xor w31, w31, w31 + + /* Read private key shares */ + bn.wsrr w7, KEY_S0_L + bn.wsrr w8, KEY_S1_L + + /* Clamp the Boolean shares */ + jal x1, x25519_clamp_shares + + /* Unmask the key */ + bn.xor w8, w7, w8 + + /* Load public key into w9 */ + li x2, 9 + la x3, x25519_public_key + bn.lid x2, 0(x3) + + jal x1, X25519 + + /* Store shared key from w22 */ + li x2, 22 + la x3, x25519_shared_key + bn.sid x2, 0(x3) + + ecall + +x25519_keygen_sideload: + /* Zeroize w31 */ + bn.xor w31, w31, w31 + + /* Read private key shares */ + bn.wsrr w7, KEY_S0_L + bn.wsrr w8, KEY_S1_L + + /* Clamp the Boolean shares */ + jal x1, x25519_clamp_shares + + /* Unmask the key */ + bn.xor w8, w7, w8 + + /* Set Curve25519 basepoint */ + bn.addi w9, w31, 9 + + jal x1, X25519 + + /* Store public key from w22 into DMEM */ + li x2, 22 + la x3, x25519_public_key + bn.sid x2, 0(x3) + + ecall + .bss /* Operation mode. */ diff --git a/sw/otbn/crypto/x25519_sideload.s b/sw/otbn/crypto/x25519_sideload.s deleted file mode 100644 index 9adec273fad1c..0000000000000 --- a/sw/otbn/crypto/x25519_sideload.s +++ /dev/null @@ -1,58 +0,0 @@ -/* Copyright lowRISC contributors (OpenTitan project). */ -/* Licensed under the Apache License, Version 2.0, see LICENSE for details. */ -/* SPDX-License-Identifier: Apache-2.0 */ - -/** - * This application runs X25519 with a sideloaded secret key. - * - * Computes X25519(k, u) according to RFC 7748, where: - * - k is the 256-bit value derived from the sideloaded key - * - u is the caller-provided Montgomery u-coordinate. - * - * The key manager provides 384 bits of sideloaded data, expressed in 2 shares - * across the special registers KEY_S0_L, KEY_S0_H, KEY_S1_L, and KEY_S1_H. - * Since we only need 256 bits, the extra bits in KEY_S0_H and KEY_S1_H are - * ignored and the encoded value of k, enc(k), is equal to KEY_S0_L ^ KEY_S1_L. - * - * The caller must check that the key manager has finished generating the key - * before attempting to run this application. - */ - -.section .text.start - -main: - /* w7 <= KEY_S0_L */ - bn.wsrr w7, 4 - /* w8 <= KEY_S1_L */ - bn.wsrr w8, 6 - /* w8 <= KEY_S0_L ^ KEY_S1_L = enc(k) */ - bn.xor w8, w7, w8 - - /* w9 <= dmem[enc_u] = enc(u) */ - li x2, 9 - la x3, enc_u - bn.lid x2, 0(x3) - - /* w22 <= enc(X25519(k, u)) */ - jal x1, X25519 - - /* dmem[enc_result] <= w22 = enc(X25519(k, u)) */ - li x2, 22 - la x3, enc_result - bn.sid x2, 0(x3) - - ecall - -.data - -/* Input Montgomery u-coordinate (encoded, 256 bits). */ -.globl enc_u -.balign 32 -enc_u: -.zero 32 - -/* Output Montgomery u-coordinate (encoded, 256 bits). */ -.globl enc_result -.balign 32 -enc_result: -.zero 32