-
Notifications
You must be signed in to change notification settings - Fork 71
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
Add list keys #261
Conversation
5d095b2
to
fc357e5
Compare
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 adding support for this! I added a few comments of another way to do it that fits better (I think) with our current architecture.
src/providers/core_provider/mod.rs
Outdated
@@ -73,6 +85,45 @@ impl Provide for CoreProvider { | |||
}) | |||
} | |||
|
|||
fn list_keys(&self, _op: list_keys::Operation) -> Result<list_keys::Result> { |
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 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).
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.
ACK, will add this 😄
src/providers/mod.rs
Outdated
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>>; |
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 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.
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 fair enough if this is a design choice we're rolling with -- will revert. 🙂
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 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.
src/providers/mod.rs
Outdated
/// 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>; | ||
|
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.
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.
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.
Gotcha. Will change this too.
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 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.
6d7b198
to
93465d3
Compare
4432672
to
0e74e42
Compare
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 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 🎸 ?
src/key_info_managers/mod.rs
Outdated
/// # Errors | ||
/// | ||
/// Returns an error as a String if there was a problem accessing the Key Info Manager. | ||
fn list_key_info( |
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 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( |
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 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 = |
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.
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.
0e74e42
to
12b983e
Compare
@joechrisellis gave me permission to continue the work on his fork, so I went ahead and addressed the comments of my own review 🤓 |
src/back/backend_handler.rs
Outdated
/// - 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() { |
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 (self.provider_id == ProviderID::Core) ^ header.opcode.is_core() { | |
if (self.provider_id == ProviderID::Core) != header.opcode.is_core() { |
Seems easier to read/understand
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 though with !=
it will not go into the if
in the following counter-example:
self.provider_id
isProvider::Tpm
header.opcode
isOpcode::Ping
edit: nvm I am wrong
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 will update, your version is definitely better
12b983e
to
1c38ee8
Compare
1c38ee8
to
f694e00
Compare
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) | ||
} |
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.
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>
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.
The reason why I did not include it is to prevent overwriting this method. I think the result is the same?
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.
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.
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.
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.
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.
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.
src/providers/tpm/mod.rs
Outdated
@@ -1,22 +1,23 @@ | |||
// Copyright 2019 Contributors to the Parsec project. | |||
//KeyInfoStore 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.
?!?!
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.
vim
played me like a fool once again
f694e00
to
b81ee59
Compare
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.
👍
Co-authored-by: Hugues de Valon <[email protected]> Signed-off-by: Joe Ellis <[email protected]>
b81ee59
to
f58a43f
Compare
Implementation for
ListKeys
. This won't immediately pass CI because it's pending a version bump onparsec-interface-rs
.