Skip to content

Add list keys #261

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

Merged
merged 1 commit into from
Oct 15, 2020
Merged

Add list keys #261

merged 1 commit into from
Oct 15, 2020

Conversation

joechrisellis
Copy link
Contributor

Implementation for ListKeys. This won't immediately pass CI because it's pending a version bump on parsec-interface-rs.

Copy link
Member

@hug-dev hug-dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for adding support for this! I added a few comments of another way to do it that fits better (I think) with our current architecture.

@@ -73,6 +85,45 @@ impl Provide for CoreProvider {
})
}

fn list_keys(&self, _op: list_keys::Operation) -> Result<list_keys::Result> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This operation should only list the keys belonging to a specific ApplicationName, so you should also add this parameter here and decode it from the auth payload in the back end handler (as it is done for crypto operations on keys).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ACK, will add this 😄

psa_hash_compute, psa_import_key, psa_raw_key_agreement, psa_sign_hash, psa_verify_hash,
};
use parsec_interface::requests::{ResponseStatus, Result};

type KeyInfoStore = Arc<RwLock<dyn ManageKeyInfo + Send + Sync>>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally do not like type aliasing that much... They kind of make it one step harder to find what the type really is and offer no static checks. I have previously made review comments similar like this on other code so we are trying to not use them too much.

I see the advantage of making the interfaces easier to read but it is not worth it to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fair enough if this is a design choice we're rolling with -- will revert. 🙂

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess it's a balance between readability and ease of use. It's kinda similar to the reason why auto exists in C++, to make convoluted types opaque.

They kind of make it one step harder to find what the type really is

That's only true if you don't use an IDE 😛 I prefer the aliasing in cases like this where there are a lot of qualifiers and a type name ends up larger than the rest of a function signature, but I'm not that bothered about it one way or another.

Comment on lines 118 to 123
/// Get the Provider ID the provider.
fn provider_id(&self) -> ProviderID;

/// Get an optional reference to the KeyInfoStore for this provider. Providers that do not
/// implement a key store should return None.
fn get_key_info_store(&self) -> Option<&KeyInfoStore>;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Provide trait should ideally only contain the actual operations that the providers can perform with the exception of when it would be really hard/unpractical to do otherwise like with the describe function above. But that one should maybe be in another trait!

My point is that, from the CoreProvider, you can directly get this information by adding a field containing a vector of all ManageKeyInfo implementation and use that trait to get the key information. You can add all of them to the CoreProvider during service building.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gotcha. Will change this too.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where a list_keys implementation on every provider would've helped 🤡 CoreProvider would then just aggregate.

Also, I'd rather go down the route of asking the provider to tell you which ManageKeyInfo it's using, because otherwise imagine having two different key info stores and switching between them at some point. The ListKeys would then report keys that you don't have access to - you see them because they're saved in a different key info manager, but the actual provider can't see them.

@joechrisellis joechrisellis force-pushed the add-list-keys branch 2 times, most recently from 6d7b198 to 93465d3 Compare October 9, 2020 10:36
@joechrisellis joechrisellis force-pushed the add-list-keys branch 4 times, most recently from 4432672 to 0e74e42 Compare October 9, 2020 11:42
Copy link
Member

@hug-dev hug-dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That looks good! One suggestion to only need one function from the Provide trait.
Also could you add an end-to-end test which creates a few keys, list them and expect the correct result 🎸 ?

/// # Errors
///
/// Returns an error as a String if there was a problem accessing the Key Info Manager.
fn list_key_info(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I would prefer having this method outside of the trait so that it can not be overwritten:

fn list_key_info(
    &impl ManageKeyInfo,
    app_name: &ApplicationName,
    provider_id: ProviderID,
)

@@ -132,6 +142,16 @@ pub trait Provide {
Err(ResponseStatus::PsaErrorNotSupported)
}

/// Lists all keys belonging to the application.
fn list_keys(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be nicer to only have the list_keys method and:

  • in all the crypto providers make it the same as what find_keys currently does
  • in the code provider call list_keys on all provider

Although we need to be sure somewhere that ListKeys can not be called directly on the crypto providers. Probably somewhere higher on the stack.

@@ -207,6 +207,14 @@ impl BackEndHandler {
trace!("list_authenticators egress");
self.result_to_response(NativeResult::ListAuthenticators(result), header)
}
NativeOperation::ListKeys(op_list_keys) => {
let app_name =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check I was talking in the other comment could be here but could also be in the is_capable method where we would check if the request's opcode is a Core or a Crypto operation and based on that check if the provider can do it or not.

@hug-dev
Copy link
Member

hug-dev commented Oct 14, 2020

@joechrisellis gave me permission to continue the work on his fork, so I went ahead and addressed the comments of my own review 🤓

/// - if the provider ID does not match, returns `ResponseStatus::WrongProviderID`
/// - if the content type does not match, returns `ResponseStatus::ContentTypeNotSupported`
/// - if the accept type does not match, returns `ResponseStatus::AcceptTypeNotSupported`
pub fn is_capable(&self, request: &Request) -> Result<()> {
let header = &request.header;

if (self.provider_id == ProviderID::Core) ^ header.opcode.is_core() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (self.provider_id == ProviderID::Core) ^ header.opcode.is_core() {
if (self.provider_id == ProviderID::Core) != header.opcode.is_core() {

Seems easier to read/understand

Copy link
Member

@hug-dev hug-dev Oct 14, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think though with != it will not go into the if in the following counter-example:

  • self.provider_id is Provider::Tpm
  • header.opcode is Opcode::Ping

edit: nvm I am wrong

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will update, your version is definitely better

Comment on lines +155 to +184
pub fn list_keys(
manager: &dyn ManageKeyInfo,
app_name: &ApplicationName,
provider_id: ProviderID,
) -> Result<Vec<parsec_interface::operations::list_keys::KeyInfo>, String> {
use parsec_interface::operations::list_keys::KeyInfo;

let mut keys: Vec<KeyInfo> = Vec::new();
let key_triples = manager.get_all(provider_id)?;

for key_triple in key_triples {
if key_triple.app_name() != app_name {
continue;
}

let key_info = manager.get(key_triple)?;
let key_info = match key_info {
Some(key_info) => key_info,
_ => continue,
};

keys.push(KeyInfo {
provider_id: ProviderID::MbedCrypto,
name: key_triple.key_name().to_string(),
attributes: key_info.attributes,
});
}

Ok(keys)
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can actually include this in the ManageKeyInfo trait, because you only rely on methods on manager that are exposed through that trait. You'd just change manager.get<all> with self.get<all>

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason why I did not include it is to prevent overwriting this method. I think the result is the same?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to prevent overwriting this method

To prevent overwriting what?

I think the result is the same?

It is, in the end, I just tend to prefer methods to functions when the function is clearly acting on a specific type.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well I might be wrong but if a method is in a trait then implementation can either not implement this method and use the default implementation, or overwrite it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, yes, true, but we have control over that. And it might be that that's an advantage, because for a database, for example, we might be able to build a query to get only the entries that match those requirements instead of filtering ourselves.

@@ -1,22 +1,23 @@
// Copyright 2019 Contributors to the Parsec project.
//KeyInfoStore Copyright 2019 Contributors to the Parsec project.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?!?!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

vim played me like a fool once again

Copy link
Member

@ionut-arm ionut-arm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

Co-authored-by: Hugues de Valon <[email protected]>
Signed-off-by: Joe Ellis <[email protected]>
@hug-dev hug-dev merged commit 48d2f90 into parallaxsecond:master Oct 15, 2020
@hug-dev hug-dev deleted the add-list-keys branch October 15, 2020 11:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants