-
Notifications
You must be signed in to change notification settings - Fork 71
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
Move test client back in the Parsec repo #150
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for this!! A few general questions to start a discussion.
@@ -40,12 +40,13 @@ derivative = "1.0.3" | |||
version = "3.0.0" | |||
|
|||
[dev-dependencies] | |||
parsec-client-test = { git = "https://github.com/parallaxsecond/parsec-client-test", tag = "0.3.0" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏
Cargo.toml
Outdated
num_cpus = "1.10.1" | ||
picky-asn1-der = "0.2.2" | ||
picky-asn1 = "0.2.1" | ||
serde = { version = "1.0", features = ["derive"] } | ||
sha2 = "0.8.1" | ||
parsec-client = { git = "https://github.com/parallaxsecond/parsec-client-rust" } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's publish it as a crate! I think it would make sense to do it now and pull it as a normal crates.io dependence! To get rid of the different versions in dependences issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sorta relying on the CI to tell me if the client actually works 😅 if it does, will replace!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see, makes sense!
|
||
#[test] | ||
fn test_ping() -> Result<()> { | ||
let mut client = TestClient::new(); | ||
let version = client.ping(ProviderID::Core)?; | ||
let version = client.ping()?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So much nicer to not have to put the redundant ProviderID::Core
tests/test_client/abstract_client.rs
Outdated
@@ -0,0 +1,329 @@ | |||
// Copyright 2019 Contributors to the Parsec project. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2020 I think 😁
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I wasn't sure because I thought I saw you changing some to 2019
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should have changed all ranges 2019 - 2020
to 2019
but kept 2020
to 2020
. If not I made a mistake!
tests/test_client/abstract_client.rs
Outdated
|
||
/// Client structure automatically choosing a provider and high-level operation functions. | ||
#[derive(Debug)] | ||
pub struct TestClient { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
About naming, I am thinking: this structure TestClient
in test_client/abstract_client.rs
does not feel totally right. Ideally we should keep the same name of the main structure of a file and its name. Any idea?
Suggestion:
- have the folder
tests/test_clients
(note the s) - inside have
Abstract
,Stress
andRaw
structures underabstract.rs
,stress.rs
andraw.rs
. Droppingtest_client
as it is in the upper module's name 💇♂️
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
abstract
is an identifier.
Also, I'm not sure about having the clients named Abstract
, Stress
etc. - while it is true that they reside within a test_clients
module, it's confusing to take Client
part out of the structure name, as when you want to use it somewhere you shouldn't need to do let client = test_clients::Stress::new();
; even this looks dubious to me - what's Stress
exactly? I think the whole idea of namespacing works and is helpful in some circumstances, but is a bit over the top in others.
I'll rename the modules within test_clients
, but will keep the Client
part of the struct name.
tests/test_client/abstract_client.rs
Outdated
#[derive(Debug)] | ||
pub struct TestClient { | ||
op_client: BasicClient, | ||
cached_opcodes: Option<HashMap<ProviderID, HashSet<Opcode>>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is caching opcodes still helpful for our tests? We might only need to know one crypto provider ID.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We might only need to know one crypto provider ID.
And isn't that a very good example where provider discovery happens? 😸 You have only one provider in the system so the test client finds it for you.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes sure, the provider discovery is an excellent idea! I meant since we are doing all requests to one single provider, we might not need to cache this HashMap
but just be able to do the discovery in the builder and use that as the provider
field of the BasicClient?
tests/test_client/abstract_client.rs
Outdated
pub struct TestClient { | ||
op_client: BasicClient, | ||
cached_opcodes: Option<HashMap<ProviderID, HashSet<Opcode>>>, | ||
provider: Option<ProviderID>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could this be the same provider as the one in the basic client?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was thinking about that, but given how the whole provider discovery thing works, this is the way to force a test to use one specific provider, because the flow goes like this:
- you ask for an operation to be executed, say, generate key
- the client checks to see if you hardcoded a value (this
Option<ProviderID>
) - if yes, it uses that
- if no, it checks the cache for a provider that it can use
Most of our tests are provider-independent so would have to include some sort of "active provider" discovery mechanism. But it's also useful to have the option to force the client to use a particular provider. This can't be done with the implicit_provider
of the underlying BasicClient
because that one isn't optional, so you wouldn't know whether it was hardcoded by the test or if it was there from the previous request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I just realised - do we actually have any tests where we attempt to make a request against a provider that isn't active?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is the way to force a test to use one specific provider
I do not think we are actually using that feature! The only occurence of set_provider
in our tests is in basic::wrong_provider_code
to test that a crypto operation on core fails. That wouldn't be possible at the BasicClient
level anyway because of the new restrictions.
That could be useful in the all_providers
tests, if you want to execute requests on multiple providers.
This can't be done with the implicit_provider of the underlying BasicClient because that one isn't optional, so you wouldn't know whether it was hardcoded by the test or if it was there from the previous request.
Ok I think I get you, you can change the provider ID just for one request and then set it to None
again so that it uses the one originally selected by the discovery.
Can you do that with a method replace_provider
that replace the current implicit_provider
with the one given and returns the old one? That way this logic could be done in the test itself when and if it is needed in the future.
The reason I am asking is that if we remove this logic, we can actually use in all of our tests the BasicClient
methods directly for most operations and not have this wrapper in the test client. That would make the code much simpler I think.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, I just realised - do we actually have any tests where we attempt to make a request against a provider that isn't active?
I don't think so, and it would be good to add!
tests/test_client/abstract_client.rs
Outdated
op_client: BasicClient, | ||
cached_opcodes: Option<HashMap<ProviderID, HashSet<Opcode>>>, | ||
provider: Option<ProviderID>, | ||
auth: AuthenticationData, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same question here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this one makes sense to remove
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the changes and new namings!
README.md
Outdated
@@ -80,17 +80,47 @@ $ cd parsec | |||
$ RUST_LOG=info cargo run | |||
``` | |||
|
|||
Parsec Client Libraries can now communicate with the service. For example using the Rust Test client, | |||
Parsec Client Libraries can now communicate with the service. For example using the Rust client, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Thanks for handling #137 as part of this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Uhm... Now that we moved the provider out of the constructor for the client and we advise users to go through list_providers
, list_opcodes
first... I feel like this example is becoming quite heavy... it went from like 10 lines to probably 40-50 if I do the discovery part too... What do? Link to the client documentation once that's out?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's probably the best thing to do! Even more so because the example we would put here would not be checked for compilation often and might not even work at times 😬
README.md
Outdated
use parsec_client::core::psa_key_attributes::{KeyAttributes, KeyType, KeyPolicy, UsageFlags}; | ||
use sha2::Sha256; | ||
|
||
let app_identity_ = AuthenticationData::AppIdentity(String::from("my-app")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: seems like there is an extraneous "_"
README.md
Outdated
let signature = client.sign(key_name, | ||
String::from("Platform AbstRaction for SECurity").into_bytes()) | ||
.unwrap(); | ||
client.psa_generate_key(key_name.clone(), key_attrs).unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These key name cloning are so annoying :/ I am wondering if we should make the functions take a reference and clone when creating the requests. If we do I am not sure if that should be part of this layer or above (in the Rust client).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know... we can either do that or make a Rust type for key names that we implement Copy
for
@@ -16,4 +16,4 @@ manager_type = "OnDisk" | |||
provider_type = "Tpm" | |||
key_id_manager = "on-disk-manager" | |||
tcti = "mssim" | |||
owner_hierarchy_auth = "tpm_pass" | |||
owner_hierarchy_auth = "" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is that not good anymore?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
😅 my mistake, I changed that for local testing, but forgot to reset it
/// The implicit provider chosen for servicing cryptographic operations is decided through | ||
/// a call to `list_providers`, followed by choosing the first non-Core provider. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
tests/test_clients/mod.rs
Outdated
basic_client: BasicClient::new( | ||
AuthenticationData::AppIdentity(String::from("root")), | ||
ProviderID::Core, | ||
), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This constructor feels a bit like the chicken and the egg: you need an implicit provider for non-core operations but you can only do that with list_providers
and you need the basic client instance for that.
That is a client change I did not notice before but what do you think if we either take the following decisions:
- put the first-crypto provider discovery inside the
BasicClient
constructor. Not so basic now... - remove the provider argument from the constructor and always but
Core
, with doc comments saying thatlist_providers
is the first thing expected. - remove the provider argument and put an
Option<ProviderID>
in theBasicCLient
structure, initialised atNone
by the constructor. We also create a new client error which is throwm when noimplicit_provider
has been selected.
I personally like 3 best.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe we should continue this in the appropriate GitHub issue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can try and settle it here, as it's not entirely clear if it's something lacking in the BasicClient
or here.
3 makes sense, but I'm guessing you wouldn't want to change the implicit_provider
back to None
. I think that matching could be done efficiently, so I can do that as another PR in the client.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess if implicit_provider
is private, you would only change it to Some(ProviderID::...)
through methods. Also checking that it's not changing it to Core
.
One more suggestion to make it even more clear in the client: renaming implicit_provider
to implicit_crypto_provider
which what it really is!
Those things give me the feeling that there would be even more reasons to separate our structures (opcodes, provide trait, provider ID, response status) between Crypto (PSA) and Core ones.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One more suggestion to make it even more clear in the client: renaming implicit_provider to implicit_crypto_provider which what it really is!
Well... sorta, that implies we'd be checking on the setter if the provider is a crypto one or not - could do! And then we just assume it's a crypto provider.
Those things give me the feeling that there would be even more reasons to separate our structures (opcodes, provide trait, provider ID, response status) between Crypto (PSA) and Core ones.
Not really, or, I don't think it would 😞 That's because you'd still have a big old enum
, just that the underlying variants won't be Core
, Mbed
, Tpm
etc, it would be Core
, PsaCrypto
. It would be easier to match
and separate them, though
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'll keep it as implicit_provider
- there will be operations that are not explicitly crypto but which need a provider defined (the smart defaulting stuff) - and while they're not available now, it doesn't hurt to have them ready. As you were saying, I'd rather give the user an option to set it to whatever they wish, regardless of whether it is allowed for currently supported operations. It feels like more of a problem with our ProviderID
definition than with this field
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
there will be operations that are not explicitly crypto
Those operations will still be aimed at a a crypto provider though, right? They can still change it with the set_crypto_provider_id
methods I think.
My suggestion was mainly about removing the provider ID argument for the new
method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would be happy with no renaming at all but just the implicit_provider
being an Option<ProviderID>
, the new
method setting it at None
and crypto methods returning a new error if it is None
.
No need to make more checks in the set_provider
method for now.
tests/test_clients/raw_request.rs
Outdated
@@ -0,0 +1,49 @@ | |||
// Copyright 2019 Contributors to the Parsec project. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think those copyrights as well should be 2020
@@ -51,6 +52,6 @@ fn should_have_been_deleted() { | |||
client | |||
.destroy_key(key_name) | |||
.expect_err("This key should have been destroyed."), | |||
ResponseStatus::PsaErrorDoesNotExist | |||
Error::Service(ResponseStatus::PsaErrorDoesNotExist) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you want, to avoid having to wrap all ResponseStatus
in Error::Service
, you could make the test client return Result<_, ResponseStatus>
panicking if the error is not Error::Service
.
I think all of the test client requests should go to the service and back, otherwise we are not really testing the service.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't mind it that much, but I can change it to unwrap the value before returning
7f9a517
to
25036c3
Compare
This commit moves the test client back in the main Parsec repo and moves it on top of the `BasicClient` - the main core client, thus making the most out of functionality re-use. Signed-off-by: Ionut Mihalcea <ionut.mihalcea@arm.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! Let's get it in!
pub fn generate_key(&mut self, key_name: String, attributes: KeyAttributes) -> Result<()> { | ||
self.basic_client | ||
.psa_generate_key(key_name.clone(), attributes) | ||
.map_err(convert_error)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Smooth!
num_cpus = "1.10.1" | ||
picky-asn1-der = "0.2.2" | ||
picky-asn1 = "0.2.1" | ||
serde = { version = "1.0", features = ["derive"] } | ||
sha2 = "0.8.1" | ||
parsec-client = "0.1.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👏
This commit moves the test client back in the main Parsec repo and moves
it on top of the
BasicClient
- the main core client, thus making themost out of functionality re-use.
Signed-off-by: Ionut Mihalcea ionut.mihalcea@arm.com