diff --git a/sw/device/tests/crypto/cryptotest/BUILD b/sw/device/tests/crypto/cryptotest/BUILD index 39b5c3bf39149..0c94183bbb027 100644 --- a/sw/device/tests/crypto/cryptotest/BUILD +++ b/sw/device/tests/crypto/cryptotest/BUILD @@ -365,6 +365,25 @@ cryptotest( test_vectors = KMAC_TESTVECTOR_TARGETS, ) +SPHINCSPLUS_TESTVECTOR_TARGETS = [ + "//sw/host/cryptotest/testvectors/data:{}".format(target) + for target in [ + "rsp_sphincsplus_sha256_128s_simple_json", + ] +] + +SPHINCSPLUS_TESTVECTOR_ARGS = " ".join([ + "--sphincsplus-json=\"$(rootpath {})\"".format(target) + for target in SPHINCSPLUS_TESTVECTOR_TARGETS +]) + +cryptotest( + name = "sphincsplus_kat", + test_args = SPHINCSPLUS_TESTVECTOR_ARGS, + test_harness = "//sw/host/tests/crypto/sphincsplus_kat:harness", + test_vectors = SPHINCSPLUS_TESTVECTOR_TARGETS, +) + # Use the following command to run the entire test suite in a given target: # $ export OT_EXEC_ENV=silicon_owner_sival_rom_ext # $ bazel test --//signing:token=//signing/tokens:cloud_kms \ @@ -392,5 +411,6 @@ test_suite( ":sha512_kat", ":shake128_kat", ":shake256_kat", + ":sphincsplus_kat", ], ) diff --git a/sw/device/tests/crypto/cryptotest/firmware/BUILD b/sw/device/tests/crypto/cryptotest/firmware/BUILD index cab5bd67ec7cf..a6cac27e775c8 100644 --- a/sw/device/tests/crypto/cryptotest/firmware/BUILD +++ b/sw/device/tests/crypto/cryptotest/firmware/BUILD @@ -115,7 +115,6 @@ cc_library( "//sw/device/lib/crypto/impl:integrity", "//sw/device/lib/crypto/impl:keyblob", "//sw/device/lib/crypto/impl:mac", - "//sw/device/lib/crypto/include:datatypes", "//sw/device/lib/runtime:log", "//sw/device/lib/testing/test_framework:ujson_ottf", "//sw/device/lib/ujson", @@ -123,6 +122,24 @@ cc_library( ], ) +cc_library( + name = "sphincsplus", + srcs = ["sphincsplus.c"], + hdrs = ["sphincsplus.h"], + deps = [ + "//sw/device/lib/base:memory", + "//sw/device/lib/base:status", + "//sw/device/lib/crypto/impl:integrity", + "//sw/device/lib/crypto/impl:keyblob", + "//sw/device/lib/crypto/include:datatypes", + "//sw/device/lib/runtime:log", + "//sw/device/lib/testing/test_framework:ujson_ottf", + "//sw/device/lib/ujson", + "//sw/device/silicon_creator/lib/sigverify/sphincsplus:verify", + "//sw/device/tests/crypto/cryptotest/json:sphincsplus_commands", + ], +) + cc_library( name = "kmac", srcs = ["kmac.c"], @@ -347,6 +364,7 @@ FIRMWARE_DEPS = [ ":otbn_fi", ":prng_sca", ":sha3_sca", + ":sphincsplus", ":trigger_sca", "//sw/device/lib/base:csr", "//sw/device/lib/base:status", diff --git a/sw/device/tests/crypto/cryptotest/firmware/firmware.c b/sw/device/tests/crypto/cryptotest/firmware/firmware.c index e96ff9052a827..0c6d45e70a188 100644 --- a/sw/device/tests/crypto/cryptotest/firmware/firmware.c +++ b/sw/device/tests/crypto/cryptotest/firmware/firmware.c @@ -26,6 +26,7 @@ #include "sw/device/tests/crypto/cryptotest/json/otbn_fi_commands.h" #include "sw/device/tests/crypto/cryptotest/json/prng_sca_commands.h" #include "sw/device/tests/crypto/cryptotest/json/sha3_sca_commands.h" +#include "sw/device/tests/crypto/cryptotest/json/sphincsplus_commands.h" #include "sw/device/tests/crypto/cryptotest/json/trigger_sca_commands.h" // Include handlers @@ -43,6 +44,7 @@ #include "otbn_fi.h" #include "prng_sca.h" #include "sha3_sca.h" +#include "sphincsplus.h" #include "trigger_sca.h" OTTF_DEFINE_TEST_CONFIG(.enable_uart_flow_control = true); @@ -73,6 +75,9 @@ status_t process_cmd(ujson_t *uj) { case kCryptotestCommandKmac: RESP_ERR(uj, handle_kmac(uj)); break; + case kCryptotestCommandSphincsPlus: + RESP_ERR(uj, handle_sphincsplus(uj)); + break; case kCryptotestCommandAesSca: RESP_ERR(uj, handle_aes_sca(uj)); break; diff --git a/sw/device/tests/crypto/cryptotest/firmware/sphincsplus.c b/sw/device/tests/crypto/cryptotest/firmware/sphincsplus.c new file mode 100644 index 0000000000000..f9e85eccae520 --- /dev/null +++ b/sw/device/tests/crypto/cryptotest/firmware/sphincsplus.c @@ -0,0 +1,99 @@ +// 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/memory.h" +#include "sw/device/lib/base/status.h" +#include "sw/device/lib/crypto/impl/integrity.h" +#include "sw/device/lib/crypto/impl/keyblob.h" +#include "sw/device/lib/crypto/include/datatypes.h" +#include "sw/device/lib/runtime/log.h" +#include "sw/device/lib/testing/test_framework/ujson_ottf.h" +#include "sw/device/lib/ujson/ujson.h" +#include "sw/device/silicon_creator/lib/sigverify/sphincsplus/verify.h" +#include "sw/device/tests/crypto/cryptotest/json/sphincsplus_commands.h" + +status_t handle_sphincsplus_verify(ujson_t *uj) { + // Declare SPHINCS+ parameter ujson deserializer types + cryptotest_sphincsplus_hash_alg_t uj_hash_alg; + cryptotest_sphincsplus_public_key_t uj_public_key; + cryptotest_sphincsplus_message_t uj_message; + cryptotest_sphincsplus_signature_t uj_signature; + + // Deserialize ujson byte stream into SPHINCS+ parameters + TRY(ujson_deserialize_cryptotest_sphincsplus_hash_alg_t(uj, &uj_hash_alg)); + TRY(ujson_deserialize_cryptotest_sphincsplus_public_key_t(uj, + &uj_public_key)); + TRY(ujson_deserialize_cryptotest_sphincsplus_message_t(uj, &uj_message)); + TRY(ujson_deserialize_cryptotest_sphincsplus_signature_t(uj, &uj_signature)); + + if (uj_public_key.public_len != kSpxVerifyPkBytes) { + LOG_ERROR("Incorrect public key length: (expected = %d, got = %d)", + kSpxVerifyPkBytes, uj_public_key.public_len); + return INVALID_ARGUMENT(); + } + if (uj_signature.signature_len != kSpxVerifySigBytes) { + LOG_ERROR("Incorrect signature length: (expected = %d, got = %d)", + kSpxVerifySigBytes, uj_signature.signature_len); + return INVALID_ARGUMENT(); + } + + switch (uj_hash_alg) { + case kCryptotestSphincsPlusHashAlgSha256: + break; + case kCryptotestSphincsPlusHashAlgShake256: + LOG_ERROR("SPHINCS+ SHAKE-256 is currently unsupported."); + return INVALID_ARGUMENT(); + default: + LOG_ERROR("Unrecognized SPHINCS+ hash mode: %d", uj_hash_alg); + return INVALID_ARGUMENT(); + } + uint32_t exp_root[kSpxVerifyRootNumWords]; + spx_public_key_root((uint32_t *)uj_public_key.public, exp_root); + uint32_t act_root[kSpxVerifyRootNumWords]; + rom_error_t error = + spx_verify((uint32_t *)uj_signature.signature, NULL, 0, NULL, 0, NULL, 0, + (uint8_t *)uj_message.message, uj_message.message_len, + (uint32_t *)uj_public_key.public, act_root); + cryptotest_sphincsplus_verify_output_t uj_output; + switch (error) { + case kErrorOk: + uj_output = kCryptotestSphincsPlusVerifyOutputSuccess; + for (size_t i = 0; i < kSpxVerifyRootNumWords; i++) { + if (exp_root[i] != act_root[i]) { + uj_output = kCryptotestSphincsPlusVerifyOutputFailure; + break; + } + } + RESP_OK(ujson_serialize_cryptotest_sphincsplus_verify_output_t, uj, + &uj_output); + break; + case kErrorSigverifyBadSpxSignature: + OT_FALLTHROUGH_INTENDED; + case kErrorSigverifyBadSpxKey: + uj_output = kCryptotestSphincsPlusVerifyOutputFailure; + // Respond "failure" if the IUT reports an invalid argument + RESP_OK(ujson_serialize_cryptotest_sphincsplus_verify_output_t, uj, + &uj_output); + break; + default: + LOG_ERROR( + "Unexpected error value returned from spx_verify: " + "0x%x", + error); + return INTERNAL(); + } + return OK_STATUS(0); +} + +status_t handle_sphincsplus(ujson_t *uj) { + cryptotest_sphincsplus_operation_t uj_op; + TRY(ujson_deserialize_cryptotest_sphincsplus_operation_t(uj, &uj_op)); + switch (uj_op) { + case kCryptotestSphincsPlusOperationVerify: + return handle_sphincsplus_verify(uj); + default: + LOG_ERROR("Unsupported SPHINCS+ operation: %d", uj_op); + return INVALID_ARGUMENT(); + } +} diff --git a/sw/device/tests/crypto/cryptotest/firmware/sphincsplus.h b/sw/device/tests/crypto/cryptotest/firmware/sphincsplus.h new file mode 100644 index 0000000000000..f8a1c1f304502 --- /dev/null +++ b/sw/device/tests/crypto/cryptotest/firmware/sphincsplus.h @@ -0,0 +1,13 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OPENTITAN_SW_DEVICE_TESTS_CRYPTO_CRYPTOTEST_FIRMWARE_SPHINCSPLUS_H_ +#define OPENTITAN_SW_DEVICE_TESTS_CRYPTO_CRYPTOTEST_FIRMWARE_SPHINCSPLUS_H_ + +#include "sw/device/lib/base/status.h" +#include "sw/device/lib/ujson/ujson.h" + +status_t handle_sphincsplus(ujson_t *uj); + +#endif // OPENTITAN_SW_DEVICE_TESTS_CRYPTO_CRYPTOTEST_FIRMWARE_SPHINCSPLUS_H_ diff --git a/sw/device/tests/crypto/cryptotest/json/BUILD b/sw/device/tests/crypto/cryptotest/json/BUILD index 6d6d2781a588e..05ed0acc0e639 100644 --- a/sw/device/tests/crypto/cryptotest/json/BUILD +++ b/sw/device/tests/crypto/cryptotest/json/BUILD @@ -133,6 +133,13 @@ cc_library( deps = ["//sw/device/lib/ujson"], ) +cc_library( + name = "sphincsplus_commands", + srcs = ["sphincsplus_commands.c"], + hdrs = ["sphincsplus_commands.h"], + deps = ["//sw/device/lib/ujson"], +) + cc_library( name = "trigger_sca_commands", srcs = ["trigger_sca_commands.c"], diff --git a/sw/device/tests/crypto/cryptotest/json/commands.h b/sw/device/tests/crypto/cryptotest/json/commands.h index db98a366f87e6..4193290e054cd 100644 --- a/sw/device/tests/crypto/cryptotest/json/commands.h +++ b/sw/device/tests/crypto/cryptotest/json/commands.h @@ -26,6 +26,7 @@ extern "C" { value(_, OtbnFi) \ value(_, PrngSca) \ value(_, Sha3Sca) \ + value(_, SphincsPlus) \ value(_, TriggerSca) UJSON_SERDE_ENUM(CryptotestCommand, cryptotest_cmd_t, COMMAND); diff --git a/sw/device/tests/crypto/cryptotest/json/sphincsplus_commands.c b/sw/device/tests/crypto/cryptotest/json/sphincsplus_commands.c new file mode 100644 index 0000000000000..d3c449ffaf9a8 --- /dev/null +++ b/sw/device/tests/crypto/cryptotest/json/sphincsplus_commands.c @@ -0,0 +1,6 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#define UJSON_SERDE_IMPL 1 +#include "sphincsplus_commands.h" diff --git a/sw/device/tests/crypto/cryptotest/json/sphincsplus_commands.h b/sw/device/tests/crypto/cryptotest/json/sphincsplus_commands.h new file mode 100644 index 0000000000000..ad587c7d4bc3a --- /dev/null +++ b/sw/device/tests/crypto/cryptotest/json/sphincsplus_commands.h @@ -0,0 +1,52 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +#ifndef OPENTITAN_SW_DEVICE_TESTS_CRYPTO_CRYPTOTEST_JSON_SPHINCSPLUS_COMMANDS_H_ +#define OPENTITAN_SW_DEVICE_TESTS_CRYPTO_CRYPTOTEST_JSON_SPHINCSPLUS_COMMANDS_H_ +#include "sw/device/lib/ujson/ujson_derive.h" +#ifdef __cplusplus +extern "C" { +#endif + +#define SPHINCSPLUS_CMD_MAX_MESSAGE_BYTES 3300 +#define SPHINCSPLUS_CMD_MAX_SIGNATURE_BYTES 7856 +#define SPHINCSPLUS_CMD_MAX_PUBLIC_KEY_BYTES 32 + +// clang-format off + +#define SPHINCSPLUS_OPERATION(_, value) \ + value(_, Verify) +UJSON_SERDE_ENUM(CryptotestSphincsPlusOperation, cryptotest_sphincsplus_operation_t, SPHINCSPLUS_OPERATION); + +#define SPHINCSPLUS_HASH_ALG(_, value) \ + value(_, Sha256) \ + value(_, Shake256) +UJSON_SERDE_ENUM(CryptotestSphincsPlusHashAlg, cryptotest_sphincsplus_hash_alg_t, SPHINCSPLUS_HASH_ALG); + +#define SPHINCSPLUS_MESSAGE(field, string) \ + field(message, uint8_t, SPHINCSPLUS_CMD_MAX_MESSAGE_BYTES) \ + field(message_len, size_t) +UJSON_SERDE_STRUCT(CryptotestSphincsPlusMessage, cryptotest_sphincsplus_message_t, SPHINCSPLUS_MESSAGE); + +#define SPHINCSPLUS_SIGNATURE(field, string) \ + field(signature, uint8_t, SPHINCSPLUS_CMD_MAX_SIGNATURE_BYTES) \ + field(signature_len, size_t) +UJSON_SERDE_STRUCT(CryptotestSphincsPlusSignature, cryptotest_sphincsplus_signature_t, SPHINCSPLUS_SIGNATURE); + +#define SPHINCSPLUS_PUBLIC_KEY(field, string) \ + field(public, uint8_t, SPHINCSPLUS_CMD_MAX_PUBLIC_KEY_BYTES) \ + field(public_len, size_t) +UJSON_SERDE_STRUCT(CryptotestSphincsPlusPublicKey, cryptotest_sphincsplus_public_key_t, SPHINCSPLUS_PUBLIC_KEY); + +#define SPHINCSPLUS_VERIFY_OUTPUT(_, value) \ + value(_, Success) \ + value(_, Failure) +UJSON_SERDE_ENUM(CryptotestSphincsPlusVerifyOutput, cryptotest_sphincsplus_verify_output_t, SPHINCSPLUS_VERIFY_OUTPUT); + +// clang-format on + +#ifdef __cplusplus +} +#endif +#endif // OPENTITAN_SW_DEVICE_TESTS_CRYPTO_CRYPTOTEST_JSON_SPHINCSPLUS_COMMANDS_H_ diff --git a/sw/host/cryptotest/testvectors/data/BUILD b/sw/host/cryptotest/testvectors/data/BUILD index 3c65b98bbd807..31f22368c9a8f 100644 --- a/sw/host/cryptotest/testvectors/data/BUILD +++ b/sw/host/cryptotest/testvectors/data/BUILD @@ -714,25 +714,31 @@ WYCHEPROOF_RSA_PSS_SUFFIXES = { ] ] -run_binary( - name = "rsp_sphincsplus_shake256_128s_simple_json", - srcs = [ - "//sw/host/cryptotest/testvectors/data/schemas:sphincsplus_schema.json", - "@sphincsplus_kat//:sphincs-shake256-128s-simple/PQCsignKAT_64.rsp", - ], - outs = [":rsp_sphincsplus_shake256_128s_simple.json"], - args = [ - "--src", - "$(location @sphincsplus_kat//:sphincs-shake256-128s-simple/PQCsignKAT_64.rsp)", - "--dst", - "$(location :rsp_sphincsplus_shake256_128s_simple.json)", - "--schema", - "$(location //sw/host/cryptotest/testvectors/data/schemas:sphincsplus_schema.json)", - "--hash_alg", - "shake-256", - ], - tool = "//sw/host/cryptotest/testvectors/parsers:rsp_sphincsplus_parser", -) +[ + run_binary( + name = "rsp_sphincsplus_{}_128s_simple_json".format(hash_alg), + srcs = [ + "//sw/host/cryptotest/testvectors/data/schemas:sphincsplus_schema.json", + "@sphincsplus_{h}_kat//:sphincs-{h}-128s-simple/PQCsignKAT_64.rsp".format(h = hash_alg), + ], + outs = [":rsp_sphincsplus_{}_128s_simple.json".format(hash_alg)], + args = [ + "--src", + "$(location @sphincsplus_{h}_kat//:sphincs-{h}-128s-simple/PQCsignKAT_64.rsp)".format(h = hash_alg), + "--dst", + "$(location :rsp_sphincsplus_{}_128s_simple.json)".format(hash_alg), + "--schema", + "$(location //sw/host/cryptotest/testvectors/data/schemas:sphincsplus_schema.json)", + "--hash_alg", + hash_arg, + ], + tool = "//sw/host/cryptotest/testvectors/parsers:rsp_sphincsplus_parser", + ) + for (hash_alg, hash_arg) in [ + ("sha256", "sha-256"), + ("shake256", "shake-256"), + ] +] run_binary( name = "hjson_kmac", diff --git a/sw/host/cryptotest/ujson_lib/BUILD b/sw/host/cryptotest/ujson_lib/BUILD index 77f23f6fdd418..c2ecfecb07951 100644 --- a/sw/host/cryptotest/ujson_lib/BUILD +++ b/sw/host/cryptotest/ujson_lib/BUILD @@ -47,6 +47,11 @@ ujson_rust( srcs = ["//sw/device/tests/crypto/cryptotest/json:kmac_commands"], ) +ujson_rust( + name = "sphincsplus_commands_rust", + srcs = ["//sw/device/tests/crypto/cryptotest/json:sphincsplus_commands"], +) + rust_library( name = "cryptotest_commands", srcs = [ @@ -59,6 +64,7 @@ rust_library( "src/hmac_commands.rs", "src/kmac_commands.rs", "src/lib.rs", + "src/sphincsplus_commands.rs", ], compile_data = [ ":commands_rust", @@ -69,6 +75,7 @@ rust_library( ":hash_commands_rust", ":hmac_commands_rust", ":kmac_commands_rust", + ":sphincsplus_commands_rust", ], rustc_env = { "commands_loc": "$(execpath :commands_rust)", @@ -79,6 +86,7 @@ rust_library( "hash_commands_loc": "$(execpath :hash_commands_rust)", "hmac_commands_loc": "$(execpath :hmac_commands_rust)", "kmac_commands_loc": "$(execpath :kmac_commands_rust)", + "sphincsplus_commands_loc": "$(execpath :sphincsplus_commands_rust)", }, deps = [ "//sw/host/opentitanlib", diff --git a/sw/host/cryptotest/ujson_lib/src/lib.rs b/sw/host/cryptotest/ujson_lib/src/lib.rs index e8e75b8a25f25..ae0a67fa249d1 100644 --- a/sw/host/cryptotest/ujson_lib/src/lib.rs +++ b/sw/host/cryptotest/ujson_lib/src/lib.rs @@ -9,3 +9,4 @@ pub mod ecdsa_commands; pub mod hash_commands; pub mod hmac_commands; pub mod kmac_commands; +pub mod sphincsplus_commands; diff --git a/sw/host/cryptotest/ujson_lib/src/sphincsplus_commands.rs b/sw/host/cryptotest/ujson_lib/src/sphincsplus_commands.rs new file mode 100644 index 0000000000000..072657ab4bca0 --- /dev/null +++ b/sw/host/cryptotest/ujson_lib/src/sphincsplus_commands.rs @@ -0,0 +1,4 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 +include!(env!("sphincsplus_commands_loc")); diff --git a/sw/host/tests/crypto/sphincsplus_kat/BUILD b/sw/host/tests/crypto/sphincsplus_kat/BUILD new file mode 100644 index 0000000000000..e93fbbf4944e8 --- /dev/null +++ b/sw/host/tests/crypto/sphincsplus_kat/BUILD @@ -0,0 +1,24 @@ +# Copyright lowRISC contributors (OpenTitan project). +# Licensed under the Apache License, Version 2.0, see LICENSE for details. +# SPDX-License-Identifier: Apache-2.0 + +load("//rules:ujson.bzl", "ujson_rust") +load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_library") + +package(default_visibility = ["//visibility:public"]) + +rust_binary( + name = "harness", + srcs = ["src/main.rs"], + deps = [ + "//sw/host/cryptotest/ujson_lib:cryptotest_commands", + "//sw/host/opentitanlib", + "@crate_index//:anyhow", + "@crate_index//:arrayvec", + "@crate_index//:clap", + "@crate_index//:humantime", + "@crate_index//:log", + "@crate_index//:serde", + "@crate_index//:serde_json", + ], +) diff --git a/sw/host/tests/crypto/sphincsplus_kat/src/main.rs b/sw/host/tests/crypto/sphincsplus_kat/src/main.rs new file mode 100644 index 0000000000000..c3bc994d24c8c --- /dev/null +++ b/sw/host/tests/crypto/sphincsplus_kat/src/main.rs @@ -0,0 +1,161 @@ +// Copyright lowRISC contributors (OpenTitan project). +// Licensed under the Apache License, Version 2.0, see LICENSE for details. +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::Result; +use arrayvec::ArrayVec; +use clap::Parser; +use std::fs; +use std::time::Duration; + +use serde::Deserialize; + +use cryptotest_commands::commands::CryptotestCommand; +use cryptotest_commands::sphincsplus_commands::{ + CryptotestSphincsPlusHashAlg, CryptotestSphincsPlusMessage, CryptotestSphincsPlusOperation, + CryptotestSphincsPlusPublicKey, CryptotestSphincsPlusSignature, + CryptotestSphincsPlusVerifyOutput, +}; + +use opentitanlib::app::TransportWrapper; +use opentitanlib::execute_test; +use opentitanlib::test_utils::init::InitializeTest; +use opentitanlib::test_utils::rpc::{UartRecv, UartSend}; +use opentitanlib::uart::console::UartConsole; + +#[derive(Debug, Parser)] +struct Opts { + #[command(flatten)] + init: InitializeTest, + + // Console receive timeout. + #[arg(long, value_parser = humantime::parse_duration, default_value = "10s")] + timeout: Duration, + + #[arg(long, num_args = 1..)] + sphincsplus_json: Vec, +} + +#[derive(Debug, Deserialize)] +struct SphincsPlusTestCase { + vendor: String, + test_case_id: usize, + algorithm: String, + operation: String, + hash_alg: String, + public: Vec, + message: Vec, + signature: Vec, + result: bool, +} + +fn run_sphincsplus_testcase( + test_case: &SphincsPlusTestCase, + opts: &Opts, + transport: &TransportWrapper, + fail_counter: &mut u32, +) -> Result<()> { + log::info!( + "vendor: {}, algorithm: {}, test case: {}", + test_case.vendor, + test_case.algorithm, + test_case.test_case_id + ); + let uart = transport.uart("console")?; + + CryptotestCommand::SphincsPlus.send(&*uart)?; + assert_eq!(test_case.algorithm.as_str(), "sphincs+"); + + // Send operation + match test_case.operation.as_str() { + "verify" => CryptotestSphincsPlusOperation::Verify, + _ => panic!("Unsupported SPHINCS+ operation"), + } + .send(&*uart)?; + + // Send hash algorithm + match test_case.hash_alg.as_str() { + "sha-256" => CryptotestSphincsPlusHashAlg::Sha256, + "shake-256" => CryptotestSphincsPlusHashAlg::Shake256, + _ => panic!("Unsupported hash algorithm"), + } + .send(&*uart)?; + + // Send public key + CryptotestSphincsPlusPublicKey { + public: ArrayVec::try_from(test_case.public.as_slice()) + .expect("SPHINCS+ public key was too large for device firmware configuration."), + public_len: test_case.public.len(), + } + .send(&*uart)?; + + // Send message + CryptotestSphincsPlusMessage { + message: ArrayVec::try_from(test_case.message.as_slice()) + .expect("SPHINCS+ message was too large for device firmware configuration."), + message_len: test_case.message.len(), + } + .send(&*uart)?; + + // Send signature + CryptotestSphincsPlusSignature { + signature: ArrayVec::try_from(test_case.signature.as_slice()) + .expect("SPHINCS+ signature was too large for device firmware configuration."), + signature_len: test_case.signature.len(), + } + .send(&*uart)?; + + // Get verification output + let success = match CryptotestSphincsPlusVerifyOutput::recv(&*uart, opts.timeout, false)? { + CryptotestSphincsPlusVerifyOutput::Success => true, + CryptotestSphincsPlusVerifyOutput::Failure => false, + CryptotestSphincsPlusVerifyOutput::IntValue(i) => { + panic!("Invalid SPHINCS+ verify result: {}", i) + } + }; + if test_case.result != success { + log::info!( + "FAILED test #{}: expected = {}, actual = {}", + test_case.test_case_id, + test_case.result, + success + ); + *fail_counter += 1; + } + Ok(()) +} + +fn test_sphincsplus(opts: &Opts, transport: &TransportWrapper) -> Result<()> { + let uart = transport.uart("console")?; + uart.set_flow_control(true)?; + let _ = UartConsole::wait_for(&*uart, r"Running [^\r\n]*", opts.timeout)?; + + let mut test_counter = 0u32; + let mut fail_counter = 0u32; + let test_vector_files = &opts.sphincsplus_json; + for file in test_vector_files { + let raw_json = fs::read_to_string(file)?; + let sphincsplus_tests: Vec = serde_json::from_str(&raw_json)?; + + for sphincsplus_test in &sphincsplus_tests { + test_counter += 1; + log::info!("Test counter: {}", test_counter); + run_sphincsplus_testcase(sphincsplus_test, opts, transport, &mut fail_counter)?; + } + } + assert_eq!( + 0, fail_counter, + "Failed {} out of {} tests.", + fail_counter, test_counter + ); + Ok(()) +} + +fn main() -> Result<()> { + let opts = Opts::parse(); + opts.init.init_logging(); + + let transport = opts.init.init_target()?; + execute_test!(test_sphincsplus, &opts, &transport); + Ok(()) +} diff --git a/third_party/sphincsplus/repos.bzl b/third_party/sphincsplus/repos.bzl index fc3d41b001f6f..7fe1f50cc046b 100644 --- a/third_party/sphincsplus/repos.bzl +++ b/third_party/sphincsplus/repos.bzl @@ -7,7 +7,7 @@ load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") def sphincsplus_repos(local = None): http_archive( - name = "sphincsplus_kat", + name = "sphincsplus_shake256_kat", build_file = Label("//third_party/sphincsplus:BUILD.sphincsplus_common.bazel"), sha256 = "95f5c79995ad8a3bc752c760f93ec409763cf2b23d1a7a7404219f26d665f7ab", urls = [ @@ -17,6 +17,17 @@ def sphincsplus_repos(local = None): "https://storage.googleapis.com/ot-crypto-test-vectors/sphincsplus/sphincsplus_shake256_128s_fips205-ipd.zip", ], ) + http_archive( + name = "sphincsplus_sha256_kat", + build_file = Label("//third_party/sphincsplus:BUILD.sphincsplus_common.bazel"), + sha256 = "1656a6aa06d731905ef72fcfbdf65f365d4c0fe89cbf3b24d1dbb64669a25e35", + urls = [ + # Self-hosted GCP ZIP that contains the 128s/SHAKE256 test + # vectors for the FIPS-205 Initial Public Draft (fips205-ipd), + # which does not have an official NIST-hosted release yet. + "https://storage.googleapis.com/ot-crypto-test-vectors/sphincsplus/sphincsplus_sha256_128s_fips205-ipd.zip", + ], + ) http_archive_or_local( name = "sphincsplus_fips205_ipd", local = local,