tf_fuzz: add new crypto key generation model
* Add a new model of what it means for a crypto policy to be valid. This
fixes the simulation of psa_generate_key() calls, which TF-Fuzz
currently cannot accurately predict.
The PSA Crypto properties modelled are whether an algorithm and key
type are compatible, whether a key type/ algorithm are disabled, and
what key sizes are allowed.
Although the PSA Crypto specification has additional, more complicated
requirements for policies and keys, this commit only implements what
is necessary for predicting the outcome of psa_generate_key(), and not
requirements that make a key useless but still valid or involve
features not yet in TF-M or MbedTLS.
* Improve the way in which policies are filled in, and allow policies to
be updated at simulation time using the value of a named policy asset.
Information about other assets and calls is not accessible during
parse time when the key policy is usually filled in. However, this is
required for the improved simulation of psa_generate_key calls. This
is because policy information for generate key calls come from a named
policy created earlier in the test file.
* Add valid flag to set-policy calls, allowing the creation of a random
valid policy. For example, see demo/36.test.
* Add demo/36.test. This test generates a policy with a (roughly) even
chance of being valid or invalid and then tries to generate a key
using it.
Running this test a large number of times (~300) succeeds with the
changes in this commit, showing that TF-Fuzz can now accurately
predict the outcome of psa_generate_key calls.
Change-Id: Ia40ff893db50b8d2c579d975aa23341b7aab004d
Signed-off-by: Nik Dewally <Nik.Dewally@arm.com>
diff --git a/tf_fuzz/tfz-cpp/calls/crypto_call.cpp b/tf_fuzz/tfz-cpp/calls/crypto_call.cpp
index b800baa..c9d000d 100644
--- a/tf_fuzz/tfz-cpp/calls/crypto_call.cpp
+++ b/tf_fuzz/tfz-cpp/calls/crypto_call.cpp
@@ -20,7 +20,9 @@
#include "crypto_asset.hpp"
#include "psa_call.hpp"
#include "crypto_call.hpp"
+#include "crypto_model.hpp"
+using namespace crypto_model;
/**********************************************************************************
Methods of class policy_call follow:
@@ -958,7 +960,50 @@
bool generate_key_call::copy_call_to_asset (void)
{
- return copy_call_to_asset_t<key_asset*> (this, yes_create_asset);
+ // we create the asset conditionally in simulate
+ return copy_call_to_asset_t<key_asset*> (this, dont_create_asset);
+}
+
+bool generate_key_call::simulate() {
+ bool is_policy_valid;
+
+ // NOTE: although algorithm and key-type depend on eachother for validity,
+ // this is checked during key operations not generation
+
+
+ if (policy.key_type == "PSA_KEY_TYPE_RSA_PUBLIC_KEY") {
+ is_policy_valid = false;
+ }
+ else if (policy.key_type == "PSA_KEY_TYPE_PEPPER") {
+ is_policy_valid = false;
+ } else {
+ key_type& kt = get_key_type(policy.key_type);
+ algorithm& alg = get_algorithm(policy.key_algorithm);
+ is_policy_valid = kt.is_valid_key_size(policy.n_bits);
+ }
+
+ psa_asset_usage asset_usage;
+
+ if (!is_policy_valid) {
+ // do not create a key when policy is invalid
+ exp_data.expect_failure();
+ return true;
+ }
+
+ // create an asset, and copy the information we want in it to it.
+ copy_call_to_asset_t<key_asset*> (this, yes_create_asset);
+ switch (asset_info.how_asset_found) {
+ case asset_search::created_new:
+ exp_data.expect_pass();
+
+ break;
+
+ default: // asset already exists!
+ exp_data.expect_failure();
+ break;
+ }
+
+ return true;
}
void generate_key_call::fill_in_prep_code (void)
@@ -1011,9 +1056,10 @@
return; // just to have something to pin a breakpoint onto
}
+
bool create_key_call::copy_call_to_asset (void)
{
- return copy_call_to_asset_t<key_asset*> (this, yes_create_asset);
+ return copy_call_to_asset_t<key_asset*> (this, dont_create_asset);
}
void create_key_call::fill_in_prep_code (void)
@@ -1324,6 +1370,30 @@
return; // just to have something to pin a breakpoint onto
}
+bool remove_key_call::simulate () {
+ if (!exp_data.simulation_needed()) {
+ return false;
+ }
+ switch (asset_info.how_asset_found) {
+ case asset_search::found_active:
+ case asset_search::created_new:
+ exp_data.expect_pass();
+ return true;
+
+ // if the asset never existed, its handle will be null.
+ // deleting the null handle succeeds
+ case asset_search::not_found:
+ exp_data.expect_pass();
+ return true;
+
+ case asset_search::found_deleted:
+ exp_data.expect_failure();
+ return true;
+ default:
+ exp_data.expect_failure();
+ return true;
+ }
+}
bool remove_key_call::copy_call_to_asset (void)
{
return copy_call_to_asset_t<key_asset*> (this, dont_create_asset);
diff --git a/tf_fuzz/tfz-cpp/calls/crypto_call.hpp b/tf_fuzz/tfz-cpp/calls/crypto_call.hpp
index ff43b45..9d6b457 100644
--- a/tf_fuzz/tfz-cpp/calls/crypto_call.hpp
+++ b/tf_fuzz/tfz-cpp/calls/crypto_call.hpp
@@ -465,9 +465,10 @@
public:
// Data members:
// Methods:
- bool copy_call_to_asset (void);
- void fill_in_prep_code (void);
- void fill_in_command (void);
+ bool copy_call_to_asset (void) override;
+ void fill_in_prep_code (void) override;
+ void fill_in_command (void) override;
+ bool simulate (void) override;
generate_key_call (tf_fuzz_info *test_state, long &asset_ser_no,
asset_search how_asset_found); // (constructor)
~generate_key_call (void);
@@ -488,9 +489,9 @@
public:
// Data members:
// Methods:
- bool copy_call_to_asset (void);
- void fill_in_prep_code (void);
- void fill_in_command (void);
+ bool copy_call_to_asset (void) override;
+ void fill_in_prep_code (void) override;
+ void fill_in_command (void) override;
create_key_call (tf_fuzz_info *test_state, long &asset_ser_no,
asset_search how_asset_found); // (constructor)
~create_key_call (void);
@@ -560,6 +561,7 @@
bool copy_call_to_asset (void) override;
void fill_in_prep_code (void) override;
void fill_in_command (void) override;
+ bool simulate (void) override;
remove_key_call (tf_fuzz_info *test_state, long &asset_ser_no,
asset_search how_asset_found); // (constructor)
~remove_key_call (void);
diff --git a/tf_fuzz/tfz-cpp/calls/psa_call.cpp b/tf_fuzz/tfz-cpp/calls/psa_call.cpp
index 7ca407e..c9763aa 100644
--- a/tf_fuzz/tfz-cpp/calls/psa_call.cpp
+++ b/tf_fuzz/tfz-cpp/calls/psa_call.cpp
@@ -5,10 +5,12 @@
*
*/
+#include <stdexcept>
#include <stdlib.h>
#include <iostream>
#include "boilerplate.hpp"
+#include "crypto_asset.hpp"
#include "string_ops.hpp"
#include "data_blocks.hpp"
#include "psa_asset.hpp"
@@ -71,6 +73,43 @@
return false;
}
+void psa_call::copy_policy_to_call(void) {
+
+ string name = policy.get_policy_from_policy;
+
+ // name represents the policy asset we want to use for this call.
+
+ // We don't want to use a policy asset, so we have nothing to do here.
+ if (name == ""){
+ return;
+ }
+
+ vector<psa_asset*>::iterator found_asset;
+ long x; // doesnt matter
+ asset_search status = test_state->find_or_create_policy_asset(psa_asset_search::name,psa_asset_usage::all,name,0,x,dont_create_asset,found_asset);
+
+ switch (status) {
+
+ case asset_search::found_active:
+ case asset_search::found_deleted:
+ case asset_search::found_invalid:
+ break;
+
+ default:
+ cerr << "Fatal: could not find policy asset " << name << endl;
+ exit(1);
+ }
+
+ string handle = policy.handle_str;
+ string asset_2_name = policy.asset_2_name;
+ string asset_3_name = policy.asset_3_name;
+
+ policy = found_asset[0]->policy;
+ policy.handle_str = handle;
+ policy.asset_2_name = asset_2_name;
+ policy.asset_3_name = asset_3_name;
+}
+
/**********************************************************************************
End of methods of class psa_call.
**********************************************************************************/
@@ -273,6 +312,34 @@
Methods of class crypto_call follow:
**********************************************************************************/
+bool crypto_call::simulate(void) {
+ bool has_changed = false;
+ has_changed |= simulate_ret_code();
+
+ return has_changed;
+}
+
+
+
+bool crypto_call::simulate_ret_code(void) {
+ if (!exp_data.simulation_needed()) {
+ return false;
+ }
+ switch (asset_info.how_asset_found) {
+ case asset_search::found_active:
+ case asset_search::created_new:
+ exp_data.expect_pass();
+ return true;
+ case asset_search::not_found:
+ case asset_search::found_deleted:
+ exp_data.expect_failure();
+ return true;
+ default:
+ exp_data.expect_failure();
+ return true;
+ }
+}
+
/* calc_result_code() fills in the check_code string member with the correct
result code (e.g., "PSA_SUCCESS" or whatever). This "modeling" needs to be
improved and expanded upon *massively* more or less mirroring what is seen in
@@ -309,32 +376,8 @@
find_replace_all ("$expect", formalized, check_code);
break;
- // TODO: move error code simulation to simulate()
- case expected_return_code_t::DontKnow: // Simulate
- // Figure out what the message should read:
- switch (asset_info.how_asset_found) {
- case asset_search::found_active:
- case asset_search::created_new:
- find_replace_all ("$expect",
- test_state->bplate->
- bplate_string[sst_pass_string],
- check_code);
- break;
- case asset_search::not_found:
- case asset_search::found_deleted:
- find_replace_all ("$expect", "PSA_ERROR_INVALID_HANDLE",
- check_code); // TODO: take from boilerplate
- break;
- default:
- find_replace_1st ("!=", "==",
- check_code); // like "fail", just make sure...
- find_replace_all ("$expect",
- test_state->bplate->
- bplate_string[sst_pass_string],
- check_code); // ... it's *not* PSA_SUCCESS
- break;
- }
- break;
+ case expected_return_code_t::DontKnow: // Simulate -- SHOULD NEVER HAPPEN
+ throw std::logic_error("Crypto call fill_in_result_code: return code is don't know");
}
}
@@ -358,7 +401,7 @@
asset_search how_asset_found)
: psa_call(test_state, call_ser_no, how_asset_found)
{
- // Nothing further to initialize.
+ policy = key_policy_info::create_random();
return; // just to have something to pin a breakpoint onto
}
crypto_call::~crypto_call (void)
diff --git a/tf_fuzz/tfz-cpp/calls/psa_call.hpp b/tf_fuzz/tfz-cpp/calls/psa_call.hpp
index 08d7316..9a497c0 100644
--- a/tf_fuzz/tfz-cpp/calls/psa_call.hpp
+++ b/tf_fuzz/tfz-cpp/calls/psa_call.hpp
@@ -83,6 +83,15 @@
/// never be overwritten.
virtual bool simulate (void);
+ // Update policy information in the call based on the policy
+ // asset specified in policy.get_policy_from_policy. If this is unset,
+ // the existing values are used as-is.
+ //
+ // This enables the simulation time setting of the policy.
+ //
+ // See `key_policy_info.get_policy_from_policy`.
+ void copy_policy_to_call(void);
+
// TODO: move simulation and error modelling code code into simulate().
// once this is done, remove default impl so that simulate is mandatory for
// calls.
@@ -159,7 +168,8 @@
public:
// Data members: // (low value in hiding these behind setters and getters)
// Methods:
- bool copy_asset_to_call (void);
+ bool copy_asset_to_call (void) override;
+ virtual bool simulate() override;
crypto_call (tf_fuzz_info *test_state, long &asset_ser_no,
asset_search how_asset_found); // (constructor)
~crypto_call (void);
@@ -167,12 +177,12 @@
protected:
// Data members:
// Methods:
- void fill_in_result_code (void);
+ void fill_in_result_code (void) override;
// for now, the method-overide buck stops here, but that'll probably change
+ bool simulate_ret_code(void);
+
private:
- // Data members:
- // Methods:
};
class security_call : public psa_call