Hi Daniel,
I’d like to plan the next nitrokey-rs releases and discuss which changes
we want to include.
As you know, I have experimented with directly using hidapi to talk to
devices. But as support for the upcoming Nitrokey 3 will already
require many changes, I don’t want to start another big refactoring
until Nitrokey 3 support has landed. So my suggestion would be to not
pursue this issue at the moment.
The main preparation we can make for Nitrokey 3 support is
modularization. The Nitrokey 3 firmware will be modular so that
different firmware versions can provide different applications
(administration, OTP, PWS, FIDO, Storage functionality, …). It will no
longer be possible to deduce the supported features statically based on
the model. Thus we also have to make our API more modular
I see three main areas that we should improve: device management,
authentication, and features. (The implementation details that I am
going to mention are just to give you some context. We can discuss the
details separately.)
Device Management
=================
Currently, libnitrokey has a global state and only supports
communication with a single device at a time. This led to our Manager
implementation that uses the type system and a mutex to make sure that
there is only one global manager and only one device per manager. This
is inflexible and makes it harder to extend nitrokey-rs with multiple
backends, e. g. libnitrokey and our own hidapi implementation.
Until libnitrokey properly handles multiple devices, I can imagine this
workaround: Instead of having a single manager protected by a mutex and
a single device per manager, we could have an arbitrary number of
devices that only store their USB path. A global mutex could be used to
protect command executions. Before every command execution, we connect
libnitrokey to the stored device path.
This would lead to additional hid enumerations for every command call,
but this should not be that bad because a) compared to the communication
with the smartcard, hid enumerations should be fast, and b) we typically
only execute one or two commands per session in nitrocli anyway. We
could also patch libnitrokey to reduce the number of hid enumerations
and to reuse the cached data.
Authentication
==============
Currently, we use wrapper structs to reflect authentication in the type
system. This is a bit cumbersome because it leads to duplication and it
is not possible to combine the information for user and admin
authentication.
When working with sequoia, I learned about their concept of marker
structs. For example, the Key type has the type parameters P: KeyParts
and R: KeyRole. The sealed KeyParts trait is implemented by the
PublicParts, SecretParts and UnspecifiedParts structs, the sealed
KeyRole trait by PrimaryRole, SubordinateRole and UnspecifiedRole.
This means that for example methods that require the secret can then be
implemented on Key<SecretParts, _>.
For nitrokey-rs, this could mean having two type parameters for the
Device type: U: UserAuth and A: AdminAuth. This would eliminate the
need for the wrapper types and make user and admin authentication
orthogonal.
Features
========
Currently, some features are represented by special traits (GenerateOtp,
ConfigureOtp), some by special structs (PasswordSafe), some by the
Device trait and some by the device structs (Pro, Storage, Librem). The
availability of features is statically determined by the model.
I suggest to separate the model and the features by introducing a single
Device struct. The model would merely be a property of this struct.
Features would be represented by special traits and/or structs, and
would be exposed by methods similar to:
fn one_time_passwords(&self) -> Option<Box<&dyn OneTimePasswords>>;
fn storage(&self) -> Option<&dyn Storage>;
If we want to access the storage feature, we would then just check for
the presence of that feature instead of assuming that the Nitrokey
Storage provides it and the Nitrokey Pro does not.
Conclusion
==========
My hope is that these changes would in combination allow us to have a
flexible solution that supports multiple backends and that reduces the
amount of boilerplate code for authentication wrappers and feature
implementations.
Please let me know what you think about this general strategy, and if
there are other fields that you would like to work on with the next
release.
As a minimal proof of concept, I have implemented these three changes
(without the mutex) for the OTP example on my poc-v0.10.0 branch:
https://git.sr.ht/~ireas/nitrokey-rs/tree/poc-v0.10.0/item/src/v2.rshttps://git.sr.ht/~ireas/nitrokey-rs/tree/poc-v0.10.0/item/examples/otp_v2.rs
Best,
Robin
Hi Robin,
Thanks for the detailed proposal! I agree with the sentiment of not
tackling the two big issues (hidapi and Nitrokey 3) at the same time.
From my perspective, what you outline makes sense and seems well
thought out. I like the marker struct design for user/admin authentication!
I just have one question tangentially related to the 'Features' aspect
you brought up: Will the "maximum" feature set of Nitrokey 3 be fixed or
will there be provisions for 3rd parties to add novel features that are
not known in advance? If so, how would they be represented?
Thanks,
Daniel
On 08.06.21 12:37, Robin Krahl wrote:
> Hi Daniel,> > I’d like to plan the next nitrokey-rs releases and discuss which changes> we want to include.> > As you know, I have experimented with directly using hidapi to talk to> devices. But as support for the upcoming Nitrokey 3 will already> require many changes, I don’t want to start another big refactoring> until Nitrokey 3 support has landed. So my suggestion would be to not> pursue this issue at the moment.> > The main preparation we can make for Nitrokey 3 support is> modularization. The Nitrokey 3 firmware will be modular so that> different firmware versions can provide different applications> (administration, OTP, PWS, FIDO, Storage functionality, …). It will no> longer be possible to deduce the supported features statically based on> the model. Thus we also have to make our API more modular> > I see three main areas that we should improve: device management,> authentication, and features. (The implementation details that I am> going to mention are just to give you some context. We can discuss the> details separately.)> > Device Management> =================> > Currently, libnitrokey has a global state and only supports> communication with a single device at a time. This led to our Manager> implementation that uses the type system and a mutex to make sure that> there is only one global manager and only one device per manager. This> is inflexible and makes it harder to extend nitrokey-rs with multiple> backends, e. g. libnitrokey and our own hidapi implementation.> > Until libnitrokey properly handles multiple devices, I can imagine this> workaround: Instead of having a single manager protected by a mutex and> a single device per manager, we could have an arbitrary number of> devices that only store their USB path. A global mutex could be used to> protect command executions. Before every command execution, we connect> libnitrokey to the stored device path.> > This would lead to additional hid enumerations for every command call,> but this should not be that bad because a) compared to the communication> with the smartcard, hid enumerations should be fast, and b) we typically> only execute one or two commands per session in nitrocli anyway. We> could also patch libnitrokey to reduce the number of hid enumerations> and to reuse the cached data.> > Authentication> ==============> > Currently, we use wrapper structs to reflect authentication in the type> system. This is a bit cumbersome because it leads to duplication and it> is not possible to combine the information for user and admin> authentication.> > When working with sequoia, I learned about their concept of marker> structs. For example, the Key type has the type parameters P: KeyParts> and R: KeyRole. The sealed KeyParts trait is implemented by the> PublicParts, SecretParts and UnspecifiedParts structs, the sealed> KeyRole trait by PrimaryRole, SubordinateRole and UnspecifiedRole.> This means that for example methods that require the secret can then be> implemented on Key<SecretParts, _>.> > For nitrokey-rs, this could mean having two type parameters for the> Device type: U: UserAuth and A: AdminAuth. This would eliminate the> need for the wrapper types and make user and admin authentication> orthogonal.> > Features> ========> > Currently, some features are represented by special traits (GenerateOtp,> ConfigureOtp), some by special structs (PasswordSafe), some by the> Device trait and some by the device structs (Pro, Storage, Librem). The> availability of features is statically determined by the model.> > I suggest to separate the model and the features by introducing a single> Device struct. The model would merely be a property of this struct.> Features would be represented by special traits and/or structs, and> would be exposed by methods similar to:> > fn one_time_passwords(&self) -> Option<Box<&dyn OneTimePasswords>>;> fn storage(&self) -> Option<&dyn Storage>;> > If we want to access the storage feature, we would then just check for> the presence of that feature instead of assuming that the Nitrokey> Storage provides it and the Nitrokey Pro does not.> > Conclusion> ==========> > My hope is that these changes would in combination allow us to have a> flexible solution that supports multiple backends and that reduces the> amount of boilerplate code for authentication wrappers and feature> implementations.> > Please let me know what you think about this general strategy, and if> there are other fields that you would like to work on with the next> release.> > As a minimal proof of concept, I have implemented these three changes> (without the mutex) for the OTP example on my poc-v0.10.0 branch:> https://git.sr.ht/~ireas/nitrokey-rs/tree/poc-v0.10.0/item/src/v2.rs> https://git.sr.ht/~ireas/nitrokey-rs/tree/poc-v0.10.0/item/examples/otp_v2.rs> > Best,> Robin>
Hi Daniel,
I’m glad that you agree with the proposal! I’ll start working on the
actual implementation in the next days. Then we can discuss the
details.
On 2021-06-09 15:27:01, Daniel Müller wrote:
> I just have one question tangentially related to the 'Features' aspect you> brought up: Will the "maximum" feature set of Nitrokey 3 be fixed or will> there be provisions for 3rd parties to add novel features that are not known> in advance? If so, how would they be represented?
The Nitrokey 3 will have Secure Boot, so the maximum feature set will
probably be that of the latest official firmware. I don’t know if it
will be possible to buy the device without Secure Boot to flash your own
firmware, but third parties could of course use the open-source hardware
schematics and firmware to produce their own variant. This could be
handled using the same feature mechanism (if we want to support it), but
I would look into that once the need arises.
Best,
Robin