-
Notifications
You must be signed in to change notification settings - Fork 72
Move test client back in the Parsec repo #150
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
Conversation
hug-dev
left a comment
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.
| 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
| 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!
| 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!
|
|
||
| /// 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,StressandRawstructures underabstract.rs,stress.rsandraw.rs. Droppingtest_clientas 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
hug-dev
left a comment
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
| ``` | ||
|
|
||
| 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
| 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
BasicClientconstructor. Not so basic now... - remove the provider argument from the constructor and always but
Core, with doc comments saying thatlist_providersis the first thing expected. - remove the provider argument and put an
Option<ProviderID>in theBasicCLientstructure, initialised atNoneby the constructor. We also create a new client error which is throwm when noimplicit_providerhas 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
| .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 <[email protected]>
hug-dev
left a comment
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!
| 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 [email protected]