Mir

Merge lp:~mir-team/mir/attestable-timestamps-server into lp:mir

Proposed by Brandon Schaefer
Status: Merged
Approved by: Alan Griffiths
Approved revision: no longer in the source branch.
Merged at revision: 2943
Proposed branch: lp:~mir-team/mir/attestable-timestamps-server
Merge into: lp:mir
Diff against target: 1375 lines (+951/-5)
32 files modified
CMakeLists.txt (+1/-0)
cmake/ABICheck.cmake (+2/-1)
debian/control (+28/-0)
debian/libmircookie-dev.install (+3/-0)
debian/libmircookie1.install (+1/-0)
debian/mir-test-tools.install (+1/-0)
include/cookie/mir/cookie_factory.h (+104/-0)
include/cookie/mir_toolkit/cookie.h (+44/-0)
include/server/mir/server.h (+12/-0)
include/test/mir_test_framework/executable_path.h (+1/-0)
src/CMakeLists.txt (+3/-0)
src/cookie/CMakeLists.txt (+48/-0)
src/cookie/cookie_factory.cpp (+161/-0)
src/cookie/mircookie.pc.in (+11/-0)
src/cookie/symbols.map (+9/-0)
src/include/server/mir/default_server_configuration.h (+7/-1)
src/include/server/mir/server_configuration.h (+5/-0)
src/server/CMakeLists.txt (+2/-1)
src/server/default_server_configuration.cpp (+21/-1)
src/server/server.cpp (+17/-0)
src/server/symbols.map (+2/-0)
tests/acceptance-tests/CMakeLists.txt (+3/-0)
tests/include/mir_test_framework/udev_environment.h (+26/-0)
tests/integration-tests/CMakeLists.txt (+1/-0)
tests/mir_test_framework/CMakeLists.txt (+4/-0)
tests/mir_test_framework/executable_path.cpp (+13/-0)
tests/mir_test_framework/udev_environment.cpp (+38/-1)
tests/mir_test_framework/udev_recordings/CMakeLists.txt (+3/-0)
tests/mir_test_framework/udev_recordings/laptop-keyboard-hello.evemu (+272/-0)
tests/unit-tests/CMakeLists.txt (+2/-0)
tests/unit-tests/test_mir_cookie.cpp (+105/-0)
tools/update_package_abis.sh (+1/-0)
To merge this branch: bzr merge lp:~mir-team/mir/attestable-timestamps-server
Reviewer Review Type Date Requested Status
Tyler Hicks (community) Needs Fixing
Brandon Schaefer (community) Approve
Andreas Pokorny (community) Approve
Alexandros Frantzis (community) Approve
Kevin DuBois (community) Approve
PS Jenkins bot (community) continuous-integration Approve
Alan Griffiths Approve
Chris Halse Rogers Approve
Review via email: mp+270457@code.launchpad.net

Commit message

server: Added a CookieFactory which generates macs for keyboard/motions events.

library: CookieFactory creates macs based on timestamp info

Description of the change

Splitting most the server bits from here:
https://code.launchpad.net/~mir-team/mir/attestable-timestamps/+merge/267866

The main parts are:
CookieFactory library
Integration with the server (init the cookie factory by seeding it with a random key)

To post a comment you must log in.
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

From the other review:

AFAICS libmircookie is only used by libmirserver.

What is the advantage to having a shared library over linking the code directly into libmirserver?

review: Needs Information
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

+std::string mir_test_framework::udev_recordings_path()
+{
+ std::string bin_path = MIR_BUILD_PREFIX"/bin/udev_recordings";
+ std::string share_path = MIR_INSTALL_PREFIX"/share/udev_recordings";
+
+ if (boost::filesystem::exists(bin_path))
+ return bin_path;
+ else if (boost::filesystem::exists(share_path))
+ return share_path;
+
+ BOOST_THROW_EXCEPTION(std::runtime_error("Failed to find udev_recordings in standard search locations"));
+}

Can this really be the right behaviour? An installed test binary looks in the location where it was built? Even though a developer may be making changes there to compare results with.

~~~~

+ char const* RANDOM_DEVICE_PATH = "/dev/random";
+ int const WAIT_SECONDS = 30;
...
+ int const MAX_WAIT = 4;

http://unity.ubuntu.com/mir/cppguide/index.html?showone=Constant_Names#Constant_Names

~~~~

AFAICS libmircookie is only used by libmirserver.

What is the advantage to having a shared library over linking the code directly into libmirserver?

~~~~

/mir/tests/acceptance-tests/test_mir_cookie.cpp:39:13: error: unused variable 'MAX_WAIT' [-Werror,-Wunused-const-variable]
  int const MAX_WAIT = 4;
            ^
1 error generated.

review: Needs Fixing
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

>
> +std::string mir_test_framework::udev_recordings_path()
> +{
> + std::string bin_path = MIR_BUILD_PREFIX"/bin/udev_recordings";
> + std::string share_path = MIR_INSTALL_PREFIX"/share/udev_recordings";
> +
> + if (boost::filesystem::exists(bin_path))
> + return bin_path;
> + else if (boost::filesystem::exists(share_path))
> + return share_path;
> +
> + BOOST_THROW_EXCEPTION(std::runtime_error("Failed to find udev_recordings
> in standard search locations"));
> +}
>
> Can this really be the right behaviour? An installed test binary looks in the
> location where it was built? Even though a developer may be making changes
> there to compare results with.

I should check the share folder first. Other then that... I think its correct behaviour. We still want to be able to test it in a build directory. Unless I add an env variable that we set for the test it self. Let me know which one you would like!

>
> ~~~~
>
> + char const* RANDOM_DEVICE_PATH = "/dev/random";
> + int const WAIT_SECONDS = 30;
> ...
> + int const MAX_WAIT = 4;
>
> http://unity.ubuntu.com/mir/cppguide/index.html?showone=Constant_Names#Constan
> t_Names
>

I need to read through the coding standard for mir :). Use to compiz/unity7/nux.

> ~~~~
>
> AFAICS libmircookie is only used by libmirserver.
>
> What is the advantage to having a shared library over linking the code
> directly into libmirserver?
>

This is how it currently stands but we are planning on using this for the content hub as well. So best to keep it shared now, as it will be used by something else.

> ~~~~
>
> /mir/tests/acceptance-tests/test_mir_cookie.cpp:39:13: error: unused variable
> 'MAX_WAIT' [-Werror,-Wunused-const-variable]
> int const MAX_WAIT = 4;
> ^
> 1 error generated.

Thanks! Missed removing that.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

Sorry about the long list here, most of it is about fitting in with the design styles that have grown in Mir and you would not yet be familiar with. I've voted "Needs Fixing" but many of the points might be addressed by discussion.

~~~~

src/server/default_server_configuration.cpp

There's a lot of code added here (to generate a seed) that probably doesn't belong in a translation unit that is about constructing the right objects to configure the system. I'd prefer to see it put in a separate file. (Is there any reason it can't be part of the new library?)

~~~~

+void fill_vector_with_random_data(std::vector<uint8_t>& buffer)

Could we not use a return value instead of an out parameter?

~~~~

+std::string mir_test_framework::udev_recordings_path()
+{
+ std::string share_path = MIR_INSTALL_PREFIX"/share/udev_recordings";
+ std::string bin_path = MIR_BUILD_PREFIX"/bin/udev_recordings";
+
+ if (boost::filesystem::exists(share_path))
+ return share_path;
+ else if (boost::filesystem::exists(bin_path))
+ return bin_path;
+
+ BOOST_THROW_EXCEPTION(std::runtime_error("Failed to find udev_recordings in standard search locations"));
+}

Can this be right? A development test binary looks in the location where it would be installed? Even though a developer may be making changes to the version in the build path.

The older logic (based on the executable path) seems less surprising.

~~~~

+class CookieFactory
+{
+public:
+ CookieFactory(std::vector<uint8_t> const& secret);
+ ~CookieFactory() noexcept;
+
+ MirCookie timestamp_to_cookie(uint64_t const& timestamp);
+
+ bool attest_timestamp(MirCookie const& cookie);
+
+private:
+ class CookieImpl;
+ std::unique_ptr<CookieImpl> impl;
+};

What is the reason for preferring the Cheshire Cat idiom over Interface Class? This makes it far harder to substitute alternative implementations (e.g. for testing that cookies actually are validated when necessary).

I can also imagine a desire to provide a different verification model in a (hypothetical) downstream server.

~~~~~

+void mir::Server::set_cookie_secret(std::vector<uint8_t> const& secret)
+{
+ verify_setting_allowed(self->server_config);
+ self->cookie_factory = std::make_shared<CookieFactory>(secret);
+}

It is a little surprising that this sets the cookie factory, not the secret. I can't immediately think of anything this breaks but...

Between this and the previous, I think this would fit better into Mir's design patterns if:

1. CookieFactory were an interface and CookieImpl a derived class.
2. the configuration point were override_the_cookie_factory

~~~~~

=== added file 'tests/acceptance-tests/test_mir_cookie.cpp'

This reads more like a unit test

~~~~~

+ mir_discover_tests_with_fd_leak_detection(mir_acceptance_tests LD_PRELOAD=libumockdev-preload.so.0 G_SLICE=always-malloc G_DEBUG=gc-friendly)

The acceptance tests (before and after this MP) don't need libumockdev-preload.so.0 (which is convenient when running them by hand) why change that?

~~~~~

AFAICS the_cookie_factory() isn't used. I've not looked in the parent branch - I assume use (and real acceptance tests) is coming soon?

review: Needs Fixing
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :
Download full text (4.6 KiB)

> Sorry about the long list here, most of it is about fitting in with the design
> styles that have grown in Mir and you would not yet be familiar with. I've
> voted "Needs Fixing" but many of the points might be addressed by discussion.
>
> ~~~~
>

No problem at all :), Thanks for going through in depth!

> src/server/default_server_configuration.cpp
>
> There's a lot of code added here (to generate a seed) that probably doesn't
> belong in a translation unit that is about constructing the right objects to
> configure the system. I'd prefer to see it put in a separate file. (Is there
> any reason it can't be part of the new library?)
>
> ~~~~

This is true, Im not 100% sure why it cant be in the library it self. Ill have to talk to Chris about that.

>
> +void fill_vector_with_random_data(std::vector<uint8_t>& buffer)
>
> Could we not use a return value instead of an out parameter?
>
> ~~~~

Changed!

>
> +std::string mir_test_framework::udev_recordings_path()
> +{
> + std::string share_path = MIR_INSTALL_PREFIX"/share/udev_recordings";
> + std::string bin_path = MIR_BUILD_PREFIX"/bin/udev_recordings";
> +
> + if (boost::filesystem::exists(share_path))
> + return share_path;
> + else if (boost::filesystem::exists(bin_path))
> + return bin_path;
> +
> + BOOST_THROW_EXCEPTION(std::runtime_error("Failed to find udev_recordings
> in standard search locations"));
> +}
>
> Can this be right? A development test binary looks in the location where it
> would be installed? Even though a developer may be making changes to the
> version in the build path.
>
> The older logic (based on the executable path) seems less surprising.
>
> ~~~~

Yes... Hmm so my overall thought here is do we need to always install the file to run the tests? Or should we ever be able to run the tests on a build directory? If we just want the test to run off the install location im 100% fine with that. All else I can think about is setting an env variable so we can run it to the build directory (and the test can set it).

Either way im happy to change it.

>
> +class CookieFactory
> +{
> +public:
> + CookieFactory(std::vector<uint8_t> const& secret);
> + ~CookieFactory() noexcept;
> +
> + MirCookie timestamp_to_cookie(uint64_t const& timestamp);
> +
> + bool attest_timestamp(MirCookie const& cookie);
> +
> +private:
> + class CookieImpl;
> + std::unique_ptr<CookieImpl> impl;
> +};
>
> What is the reason for preferring the Cheshire Cat idiom over Interface Class?
> This makes it far harder to substitute alternative implementations (e.g. for
> testing that cookies actually are validated when necessary).
>
> I can also imagine a desire to provide a different verification model in a
> (hypothetical) downstream server.
>
> ~~~~~

Not sure the reasoning here, Ill have to talk to Chris about it. Wouldnt be to hard to switch over to just an interface.
>
> +void mir::Server::set_cookie_secret(std::vector<uint8_t> const& secret)
> +{
> + verify_setting_allowed(self->server_config);
> + self->cookie_factory = std::make_shared<CookieFactory>(secret);
> +}
>
> It is a little surprising that this sets the cookie factory, n...

Read more...

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Chris Halse Rogers (raof) wrote :

> What is the reason for preferring the Cheshire Cat idiom over Interface Class?
> This makes it far harder to substitute alternative implementations (e.g. for
> testing that cookies actually are validated when necessary).
>
> I can also imagine a desire to provide a different verification model in a
> (hypothetical) downstream server.

libmircookie is going to be used by Content Hub; Mir will share the secret with CH, and then CH will be able to verify cookies sent to it (for copy/paste and drag/drop operations). This is why it's a split-out library.

As such, it's actively harmful for downstream servers to override the implementation, as multiple processes need to agree on the method to verify cookies.

As an added bonus feature, it also means that libmircookie can have an actual ABI that's stable even under mild code churn :).

> Can this be right? A development test binary looks in the location where it
> would be installed? Even though a developer may be making changes to the
> version in the build path.
>
> The older logic (based on the executable path) seems less surprising.

Yeah, I'd have the test look first in the bin dir and *then* in the system-install location.

> src/server/default_server_configuration.cpp
>
> There's a lot of code added here (to generate a seed) that probably doesn't
> belong in a translation unit that is about constructing the right objects to
> configure the system. I'd prefer to see it put in a separate file. (Is there
> any reason it can't be part of the new library?)

I'd have no objections to it being in the new library.

Other comments:

+ * This is not in any way cryptographically secure. This DOES NOT provide security.

We should drop this bit of the comment.

Once the above are dealt with: approve.

We should probably add a debian/libmircookie1.symbols file, but that can easily be done later.

review: Approve
Revision history for this message
Chris Halse Rogers (raof) wrote :

Oh, whitespace before '}':
362 + MirCookie cookie { timestamp, 0};

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> > I can also imagine a desire to provide a different verification model in a
> > (hypothetical) downstream server.
>
> libmircookie is going to be used by Content Hub; Mir will share the secret
> with CH, and then CH will be able to verify cookies sent to it (for copy/paste
> and drag/drop operations). This is why it's a split-out library.
>
> As such, it's actively harmful for downstream servers to override the
> implementation, as multiple processes need to agree on the method to verify
> cookies.

Oh, I agree that within a UI stack it ought to be fixed. Just imagining that someone /could/ want a different implementation in a different stack.

> As an added bonus feature, it also means that libmircookie can have an actual
> ABI that's stable even under mild code churn :).

OK, that's an argument for a separate library. In fact, is it really something that will need to be released with every Mir release? Or should it be a separate project?

BTW this last argument doesn't address the "Cheshire Cat vs Interface" point. The ABI could easily be:

std::unique_ptr<CookieFactory> CookieFactory::create_with_secret(std::vector<uint8_t> const& secret);
std::unique_ptr<CookieFactory> CookieFactory::create_without_secret();

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> > === added file 'tests/acceptance-tests/test_mir_cookie.cpp'
> >
> > This reads more like a unit test
> >
> > ~~~~~
> test_cookie_factory sound any better? (Im bad at names)

You misunderstand, I mean the test body reads like a unit test, not a test of the system.

Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

O yeah sorry, I was planning on moving all those tests to unit tests in the client code ... but i should do that now and save on diff +/- :)

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

+namespace mir
+{
...
+class CookieFactory
...
+std::vector<uint8_t> get_random_data(unsigned size);

We reserve the general mir namespace for system setup. These could be in a "cookie" namespace.

~~~~

+class CookieFactory
+{
+public:
+ CookieFactory(std::vector<uint8_t> const& secret);
+ ~CookieFactory() noexcept;
+
+ MirCookie timestamp_to_cookie(uint64_t const& timestamp);
+
+ bool attest_timestamp(MirCookie const& cookie);
+
+private:
+ class CookieImpl;
+ std::unique_ptr<CookieImpl> impl;
+};

I'm still not convinced we should prefer the Cheshire Cat idiom (over Interface Class) here, but if we are taking this approach then impl should be const.

~~~~

+std::string mir_test_framework::udev_recordings_path()
+{
+ std::string bin_path = MIR_BUILD_PREFIX"/bin/udev_recordings";
+ std::string share_path = MIR_INSTALL_PREFIX"/share/udev_recordings";
+
+ if (boost::filesystem::exists(bin_path))
+ return bin_path;
+ else if (boost::filesystem::exists(share_path))
+ return share_path;
+
+ BOOST_THROW_EXCEPTION(std::runtime_error("Failed to find udev_recordings in standard search locations"));
+}

Can this really be the right behaviour? An installed test binary looks in the location where it was built? Even though a developer may be making changes there?

The older logic (based on the executable path) seems less surprising.

~~~~

+ CookieFactory(std::vector<uint8_t> const& secret);

This will throw if the secret is too small, but the behaviour and minimum size is not documented anywhere.

I suggest providing a static class member (minimum_secret_size) and documenting the requirement.

review: Needs Fixing
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

Is libmircookie really something that will need to be released in sync with every Mir release? Or should it be a separate project?

~~~~

+std::vector<uint8_t> get_random_data(unsigned size);

Could be:

CookieFactory::CookieFactory(unsigned size);

~~~~

+void mir::Server::set_cookie_secret(std::vector<uint8_t> const& secret)
+{
+ verify_setting_allowed(self->server_config);
+ self->cookie_factory = std::make_shared<CookieFactory>(secret);
+}

It is a little surprising that this sets the cookie factory, not the secret.

Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

> Is libmircookie really something that will need to be released in sync with
> every Mir release? Or should it be a separate project?

I dont have a clear answer for this one. I just imagine since the project is pretty small it makes it easier to keep it in lp:mir atm? Will have to poke raof about that one.
>
> ~~~~
>
> +std::vector<uint8_t> get_random_data(unsigned size);
>
> Could be:
>
> CookieFactory::CookieFactory(unsigned size);
>

I had this at first, but switched up because one of the primary use-cases for CookieFactory is when you've shared the secret with another process, which you can't do if you've called CookieFactory(unsigned size)

> ~~~~
>
> +void mir::Server::set_cookie_secret(std::vector<uint8_t> const& secret)
> +{
> + verify_setting_allowed(self->server_config);
> + self->cookie_factory = std::make_shared<CookieFactory>(secret);
> +}
>
> It is a little surprising that this sets the cookie factory, not the secret.

I think this function should just be renamed to initialize_cookie_factory(..)

Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

> +namespace mir
> +{
> ...
> +class CookieFactory
> ...
> +std::vector<uint8_t> get_random_data(unsigned size);
>
> We reserve the general mir namespace for system setup. These could be in a
> "cookie" namespace.
>
> ~~~~

Done.

>
> +class CookieFactory
> +{
> +public:
> + CookieFactory(std::vector<uint8_t> const& secret);
> + ~CookieFactory() noexcept;
> +
> + MirCookie timestamp_to_cookie(uint64_t const& timestamp);
> +
> + bool attest_timestamp(MirCookie const& cookie);
> +
> +private:
> + class CookieImpl;
> + std::unique_ptr<CookieImpl> impl;
> +};
>
> I'm still not convinced we should prefer the Cheshire Cat idiom (over
> Interface Class) here, but if we are taking this approach then impl should be
> const.
>
> ~~~~

Move to an NVI idiom:
http://www.gotw.ca/publications/mill18.htm
>
> +std::string mir_test_framework::udev_recordings_path()
> +{
> + std::string bin_path = MIR_BUILD_PREFIX"/bin/udev_recordings";
> + std::string share_path = MIR_INSTALL_PREFIX"/share/udev_recordings";
> +
> + if (boost::filesystem::exists(bin_path))
> + return bin_path;
> + else if (boost::filesystem::exists(share_path))
> + return share_path;
> +
> + BOOST_THROW_EXCEPTION(std::runtime_error("Failed to find udev_recordings
> in standard search locations"));
> +}
>
> Can this really be the right behaviour? An installed test binary looks in the
> location where it was built? Even though a developer may be making changes
> there?
>
> The older logic (based on the executable path) seems less surprising.
>
> ~~~~
Not sure what the correct answer is here. What would be the best solution?

>
> + CookieFactory(std::vector<uint8_t> const& secret);
>
> This will throw if the secret is too small, but the behaviour and minimum size
> is not documented anywhere.
>
> I suggest providing a static class member (minimum_secret_size) and
> documenting the requirement.

Done.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> > +std::vector<uint8_t> get_random_data(unsigned size);
> >
> > Could be:
> >
> > CookieFactory::CookieFactory(unsigned size);
> >
>
> I had this at first, but switched up because one of the primary use-cases for
> CookieFactory is when you've shared the secret with another process, which you
> can't do if you've called CookieFactory(unsigned size)

I was thinking two constructors would be:

   explicit CookieFactory(std::vector<uint8_t> const& secret);
   explicit CookieFactory(unsigned size);

But I take your point - you need access to the "random data" to share the secret.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

> > Is libmircookie really something that will need to be released in sync with
> > every Mir release? Or should it be a separate project?
>
> I dont have a clear answer for this one. I just imagine since the project is
> pretty small it makes it easier to keep it in lp:mir atm? Will have to poke
> raof about that one.

A further thought, the header is dependent on MirCookie (introduced here into include/common/mir_toolkit/common.h). That makes client code (compile time) dependent on libmircommon-dev.

As we're introducing this dependency on mircommon, could we just put the code into libmircommon and not a separate library?

Or should MirCookie be defined in libmircookie-dev and not libmircommon-dev?

review: Needs Information
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

+#include <nettle/hmac.h>
...
+ struct hmac_sha1_ctx ctx;

Surely we don't want to introduce this dependency to user code? (See [1].)

There is no point in this class heirarchy if you are putting the derived class definition in the same header.

An option to consider here would the Named Constructor Idiom.

~~~~

+// Using the NVI Idiom

Nobody takes NVPI seriously - it is a solution in search of a problem.

The principle options to consider are Interface and Cheshire Cat (a.k.a. Pimpl). For a comparison of the different options see[2].

~~~~~

+ CookieFactoryNettle(std::vector<uint8_t> const& secret);

This will throw if the secret is too small, but the behaviour and minimum size is not documented in a doc comment on the function. (Having a non-doc comment in the private section of the class doesn't help the user.)

(But as I don't think this class should be made public anyway the documentation should go on the corresponding factory function.)

~~~~

> > The older logic (based on the executable path) seems less surprising.
> >
> > ~~~~
>
> Not sure what the correct answer is here. What would be the best solution?

I think to first check relative to the executable path (as it did before this MP), then to check the install location.

E.g. if running from a build directory it should pick up the one from e.g. <build>/bin/udev_recordings/ if not it picks up the installed version.

~~~~

[1] http://wiki.hsr.ch/Prog3/files/overload72-FINAL_DesigningHeaderFiles.pdf

[2] http://www.twonine.co.uk/articles/SeparatingInterfaceAndImplementation.pdf

review: Needs Fixing
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

> +#include <nettle/hmac.h>
> ...
> + struct hmac_sha1_ctx ctx;
>
> Surely we don't want to introduce this dependency to user code? (See [1].)
>
> There is no point in this class heirarchy if you are putting the derived class
> definition in the same header.
>
> An option to consider here would the Named Constructor Idiom.

Done.

>
> ~~~~
>
> +// Using the NVI Idiom
>
> Nobody takes NVPI seriously - it is a solution in search of a problem.
>
> The principle options to consider are Interface and Cheshire Cat (a.k.a.
> Pimpl). For a comparison of the different options see[2].
>
> ~~~~~

Done.

>
> + CookieFactoryNettle(std::vector<uint8_t> const& secret);
>
> This will throw if the secret is too small, but the behaviour and minimum size
> is not documented in a doc comment on the function. (Having a non-doc comment
> in the private section of the class doesn't help the user.)
>
> (But as I don't think this class should be made public anyway the
> documentation should go on the corresponding factory function.)
>
> ~~~~

Done.

>
> > > The older logic (based on the executable path) seems less surprising.
> > >
> > > ~~~~
> >
> > Not sure what the correct answer is here. What would be the best solution?
>
> I think to first check relative to the executable path (as it did before this
> MP), then to check the install location.
>
> E.g. if running from a build directory it should pick up the one from e.g.
> <build>/bin/udev_recordings/ if not it picks up the installed version.
>
> ~~~~

Done.

>
> [1] http://wiki.hsr.ch/Prog3/files/overload72-FINAL_DesigningHeaderFiles.pdf
>
> [2] http://www.twonine.co.uk/articles/SeparatingInterfaceAndImplementation.pdf

Thanks! Those were good reads. If you've more articles, I would be more then happy to read :).

Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

> > > Is libmircookie really something that will need to be released in sync
> with
> > > every Mir release? Or should it be a separate project?
> >
> > I dont have a clear answer for this one. I just imagine since the project is
> > pretty small it makes it easier to keep it in lp:mir atm? Will have to poke
> > raof about that one.
>
> A further thought, the header is dependent on MirCookie (introduced here into
> include/common/mir_toolkit/common.h). That makes client code (compile time)
> dependent on libmircommon-dev.
>
> As we're introducing this dependency on mircommon, could we just put the code
> into libmircommon and not a separate library?
>
> Or should MirCookie be defined in libmircookie-dev and not libmircommon-dev?

Well I dont see why it couldnt be defined in libmircookie-dev. I think *if* we keep libmircookie-dev we should move the MirCookie into it (unless Im missing something for why this isnt possible). If we move to libmircommon-dev then we'll keep it :).

Need to talk with RAOF about it as well

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Needs Fixing (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

+class CookieFactory
...

Should delete copy constructor and assignment.

~~~~

+ static unsigned const minimum_secret_size;

1. Ought to be public, so that it can be accessed by client code.

2. Not providing the value in the header allows it to be changed without breaking ABI, but it would probably be more convenient for users to have the value visible.

~~~~

+ * Contruction function used to create a CookieFactory. The secret size must be
+ * greater then minimum_secret_size otherwise an expection will be thrown
...
+ * Contruction function used to create a CookieFactory as well as a secret.
+ * The secret size must be greater then minimum_secret_size otherwise an expection will be thrown

s/greater then/no less than/

~~~~

+/**
+* Checks entropy exists on the system, then fills in a vector with random numbers
+* up to size.
+*
+* \param [in] size The number of random numbers to generate.
+* \return A filled in vector with random numbers up to size
+*/
+std::vector<uint8_t> get_random_data(unsigned size);

Not needed in the API

~~~~

+unsigned const min_secret_size{8};

This isn't needed, minimum_secret_size should be initialized directly.

~~~~~

+namespace
+{
+ unsigned const secret_size{64};
+}

There ought to be a guarantee (e.g. a static assert) that this is greater than CookieFactory::minimum_secret_size. (And that is why I think CookieFactory::minimum_secret_size should be public and evaluate at compile time.)

~~~~

+TEST(MirCookieFactory, throw_when_secret_size_to_small)
+{
+ std::vector<uint8_t> bob{ 0x01 };
+ EXPECT_THROW({
+ auto factory = mir::cookie::CookieFactory::create(bob);
+ }, std::logic_error);
+}

OK, but it is better to test the "off by one" case. I.e. CookieFactory::minimum_secret_size (as the documentation says) or CookieFactory::minimum_secret_size-1 (as the code says).

~~~~

There are missing tests:

1. create(unsigned secret_size, std::vector<uint8_t>& save_secret);

This should be tested to check that a secret of the right size is saved, and that it throws if too small a size is requested.

2. get_random_data(unsigned size);

*If* this is in the API there should be corresponding tests.

~~~~

+MIR_COOKIE_1 {
+ global:
+ extern "C++" {
+ mir::cookie::CookieFactory::CookieFactory*;
+ mir::cookie::CookieFactory::?CookieFactory*;
+ mir::cookie::CookieFactory::create*;
+ mir::cookie::CookieFactory::timestamp_to_cookie*;
+ mir::cookie::CookieFactory::attest_timestamp*;
+ mir::cookie::get_random_data*;
+ };
+ local: *;
+};

The only functions that need to be exported are mir::cookie::CookieFactory::create*; (and, if it is in the API mir::cookie::get_random_data*;)

review: Needs Fixing
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

Just a bit of musing, not anything that requires action:

The create functions could be:

std::unique_ptr<CookieFactory> create_from_secret(std::vector<uint8_t> const& save_secret);
std::unique_ptr<CookieFactory> create_saving_secret(std::vector<uint8_t>& save_secret, unsigned secret_size = 2*minimum_secret_size);

That might be more convenient for code that doesn't care about the secret size. There could also be other variants:

std::unique_ptr<CookieFactory> create_keeping_secret(unsigned secret_size = 2*minimum_secret_size);

Which would support user code that just does:

    auto const cf = CookieFactory::create_keeping_secret();

Maybe not an important use case?

~~~~

It could also be convenient to include a definition of Secret:

    using Secret = std::vector<uint8_t>;

and use that in both the interface and user code.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

+TEST(MirCookieFactory, timestamp_trusted_with_saved_secret_does_attest)
+{
+ uint64_t timestamp = 23;
+ unsigned secret_size = 64;
+ std::vector<uint8_t> secret;
+
+ auto factory = mir::cookie::CookieFactory::create_saving_secret(secret, secret_size);
+ auto cookie = factory->timestamp_to_cookie(timestamp);
+
+ EXPECT_TRUE(factory->attest_timestamp(cookie));
+}

Not unreasonable, but I was actually thinking of...

    uint64_t timestamp = 23;
    unsigned secret_size = 64;
    std::vector<uint8_t> secret;

    auto const source_factory = mir::cookie::CookieFactory::create_saving_secret(secret, secret_size);
    auto const sink_factory = mir::cookie::CookieFactory::create_from_secret(secret);
    auto cookie = source_factory->timestamp_to_cookie(timestamp);

    EXPECT_TRUE(sink_factory->attest_timestamp(cookie));

Which as one of the use cases we intend to support is something we should be testing.

Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

For me, this is the remaining blocker:

> > > > Is libmircookie really something that will need to be released in sync
> > with
> > > > every Mir release? Or should it be a separate project?
> > >
> > > I dont have a clear answer for this one. I just imagine since the project
> is
> > > pretty small it makes it easier to keep it in lp:mir atm? Will have to
> poke
> > > raof about that one.
> >
> > A further thought, the header is dependent on MirCookie (introduced here
> into
> > include/common/mir_toolkit/common.h). That makes client code (compile time)
> > dependent on libmircommon-dev.
> >
> > As we're introducing this dependency on mircommon, could we just put the
> code
> > into libmircommon and not a separate library?
> >
> > Or should MirCookie be defined in libmircookie-dev and not libmircommon-dev?
>
> Well I dont see why it couldnt be defined in libmircookie-dev. I think *if* we
> keep libmircookie-dev we should move the MirCookie into it (unless Im missing
> something for why this isnt possible). If we move to libmircommon-dev then
> we'll keep it :).
>
> Need to talk with RAOF about it as well

review: Needs Information
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

It would be good for cookie clients to have a narrow, stable API.

To which end we should keep libmircookie as a separate project and make it independent of libmircommon-dev.

review: Needs Fixing
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

> It would be good for cookie clients to have a narrow, stable API.
>
> To which end we should keep libmircookie as a separate project and make it
> independent of libmircommon-dev.

Done. Is it fine to keep the entire mir cookie project inside lp:mir or should a new project be created?

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

On Wednesday, 16 September 2015 20:47:24 BST, Brandon Schaefer wrote:
>> It would be good for cookie clients to have a narrow, stable API.
>>
>> To which end we should keep libmircookie as a separate project
>> and make it
>> independent of libmircommon-dev.
>
> Done. Is it fine to keep the entire mir cookie project inside
> lp:mir or should a new project be created?

Fine in mir tree. (For now anyway)

--
Alan Griffiths. +44 (0)798 9938 758
Octopull Limited. http://www.octopull.co.uk/

Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

Hmm the only issue im not 100% sure about now. Is how we are going to expose the MirCookie to the mir_toolkit C api. I suppose Ill need to create a C api for the CookieFactory just to hold the MirCookie?

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

Pushed trivial fixes:

1. src/CMakeLists.txt to build cookie independently of any other Mir code
2. include/cookie/mir_toolkit/cookie.h to compile standalone and add to mir_toolkit docs
3. include/cookie/mir/cookie_factory.h include include/cookie/mir_toolkit/cookie.h first (to ensure it compiles standalone) and move doc comment to correct type.

review: Approve
Revision history for this message
Alan Griffiths (alan-griffiths) wrote :

NB I think the server API needs extending to retrieve the generated secret, but that can come in the next MP.

Revision history for this message
PS Jenkins bot (ps-jenkins) wrote :
review: Approve (continuous-integration)
Revision history for this message
Kevin DuBois (kdub) wrote :

+ static std::unique_ptr<CookieFactory> create_saving_secret(Secret& save_secret,
+ unsigned secret_size = 2 * minimum_secret_size);
+ static std::unique_ptr<CookieFactory> create_keeping_secret(unsigned secret_size = 2 * minimum_secret_size);

http://unity.ubuntu.com/mir/cppguide/index.html?showone=Default_Arguments#Default_Arguments

not too flummoxed by that, the rest lgtm

review: Approve
Revision history for this message
Alexandros Frantzis (afrantzis) wrote :

Looks good.

Not blocking, but I find the following API simpler to understand:

create_with_new_secret(unsigned int secret_size);
create_with_existing_secret(Secret const& secret);
Secret secret();

Of course, with this API the secret is no longer really "secret" (i.e. internal to the class), but I don't think it matters. If one has access to the class, then it doesn't really matter if one also has the secret (for our use cases at least).

review: Approve
Revision history for this message
Andreas Pokorny (andreas-pokorny) wrote :

Overall looks good for me.

This has changed quite a bit since the last time. When do we need override_the_cookie_factory(secret)?

nit :+ 470 braces of lambda functor..

review: Approve
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

> Looks good.
>
> Not blocking, but I find the following API simpler to understand:
>
> create_with_new_secret(unsigned int secret_size);
> create_with_existing_secret(Secret const& secret);
> Secret secret();
>
> Of course, with this API the secret is no longer really "secret" (i.e.
> internal to the class), but I don't think it matters. If one has access to the
> class, then it doesn't really matter if one also has the secret (for our use
> cases at least).

A main use of the API is to share the secret around once its been created. So we need to be able to expose the secret on creation so we can share it with another factory.

Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

LGTM

review: Approve
Revision history for this message
Brandon Schaefer (brandontschaefer) wrote :

> Overall looks good for me.
>
> This has changed quite a bit since the last time. When do we need
> override_the_cookie_factory(secret)?
>

This will be used in the client side of things. Not a perfect split from server/client but it will be used :).

> nit :+ 470 braces of lambda functor..

Revision history for this message
Tyler Hicks (tyhicks) wrote :

Hello - A review from the security team was requested and I've started to look at the code. However, I feel like I'm missing a lot of context from the greater design. It seems like this is only the very low-level building blocks needed by the greater design.

1) Does any documentation exist on the design? Thomas spoke with us about potential high-level designs months ago and I roughly understand which direction you all chose but this HMAC design was not one that we discussed at that time.

2) How will content-hub register itself with Mir? What's the flow of cookies between Mir, the foreground app, and content-hub during copy and paste events?

3) What prevents malicious apps from attempting to brute force cookies? I don't see any type of penalty implementation for repeated, bad guesses. Is content-hub expected to implement that?

review: Needs Information
Revision history for this message
Chris Halse Rogers (raof) wrote :

1) The copy/paste doc is at https://docs.google.com/document/d/16qCQ8vYJmS7da1yfzBuKWnlUCrw_IXXsTIhWruPN0nk

2) The design is for content-hub to share a secret with Mir, and each construct their own instance of CookieFactory from that secret. Content-hub does not need to register itself with Mir. The copy/paste doc has some cookie flow examples.

3) The Mir calls which accept a MirCookie¹ will disconnect any client that submits an invalid cookie. Content-hub should probably do the same. Would it be better to (a) document that any attestation failure should be fatal to the client, and (b) rename “bool attest_timestamp(MirCookie const& cookie)” to “assert_timestamp()” and have it throw an exception on failure?

¹: Here's an example: https://code.launchpad.net/~mir-team/mir/cookie-raise-surface/+merge/274728

Revision history for this message
Tyler Hicks (tyhicks) wrote :

1) Thanks! The copy/paste doc helps a lot but it lacks implementation details. For example, "secret" isn't mentioned anywhere in the document. It does get me closer to understanding the design.

2) I'm confused by the "Content-hub does not need to register itself with Mir" bit since content-hub must share a secret with Mir and then Mir's CookieFactory is constructed from that secret. How does Mir know that the CookieFactory that it constructed was done so with a secret from Content-hub and not some other process?

3) If Mir disconnects a client that submits an invalid cookie, what prevents the client from reconnecting and submitting another invalid cookie?
  - Documenting that any attestation failure should be fatal to the client would be a good thing as long as a reconnecting is expensive.
  - IIUC, assert_timestamp() is in the address space of the client, correct? If so, throwing an exception on failure would do no good because a malicious client could link against a modified library that doesn't throw an exception.

review: Needs Information
Revision history for this message
Chris Halse Rogers (raof) wrote :

1, 2) Yeah, that bit of the design is unspecified. I was assuming that we'd either go for:
 a) A parent process of unity8 and content-hub generates a secret and fd-passes it to both, or
 b) Unity8 and content-hub share a file readable only by them, or
 c) content-hub is started by unity8 and gets the secret fd-passed to it.

I'm not sufficiently familiar with the global design to know which one is most appropriate.

3) Unity8 only allows one connection per client. Actually, that's a lie; Unity8 effectively allows any client to connect to it, but in *theory* Unity8 only allows one connection per client. In theory, unity8 only allows applications started by upstart-app-launch and only allows one connection per launch, but in practice it allows anything that has --desktop-file-hint=valid.desktop on its commandline, or any binary whose name starts with maliit-server or which contains qt5/libexec/QtWebProcess.

So reconnection *should* be expensive, but currently isn't.

assert_timestamp() is in the server's address space; the secret required to assert a timestamp isn't available in the client address space.

Revision history for this message
Tyler Hicks (tyhicks) wrote :

1, 2) Ok, I'll follow up with tvoss on that aspect of the design.

3) We could probably do something as simple as adding a penalty delay after each failed attest attempt, before disconnecting the client.

Note that I also included a couple inline questions.

review: Needs Information
Revision history for this message
Chris Halse Rogers (raof) wrote :

3) It'd be reasonably complex to add a penalty delay after a failed attest attempt, but possible. It's much simpler for us to just disconnect the client.

Replies inline.

Revision history for this message
Tyler Hicks (tyhicks) wrote :

Left some inline replies/comments.

review: Needs Fixing
Revision history for this message
Tyler Hicks (tyhicks) wrote :

One more inline comment...

Revision history for this message
Chris Halse Rogers (raof) wrote :

Replies also inline.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'CMakeLists.txt'
--- CMakeLists.txt 2015-08-12 09:52:57 +0000
+++ CMakeLists.txt 2015-09-17 09:09:28 +0000
@@ -175,6 +175,7 @@
175find_package(LTTngUST REQUIRED)175find_package(LTTngUST REQUIRED)
176pkg_check_modules(UDEV REQUIRED libudev)176pkg_check_modules(UDEV REQUIRED libudev)
177pkg_check_modules(GLIB REQUIRED glib-2.0)177pkg_check_modules(GLIB REQUIRED glib-2.0)
178pkg_check_modules(NETTLE REQUIRED nettle)
178pkg_check_modules(UUID REQUIRED uuid)179pkg_check_modules(UUID REQUIRED uuid)
179180
180include_directories (${GLESv2_INCLUDE_DIRS})181include_directories (${GLESv2_INCLUDE_DIRS})
181182
=== modified file 'cmake/ABICheck.cmake'
--- cmake/ABICheck.cmake 2015-07-23 02:39:20 +0000
+++ cmake/ABICheck.cmake 2015-09-17 09:09:28 +0000
@@ -79,6 +79,7 @@
79make_lib_descriptor(client)79make_lib_descriptor(client)
80make_lib_descriptor(server)80make_lib_descriptor(server)
81make_lib_descriptor(common INCLUDE_PRIVATE EXCLUDE_HEADERS ${mircommon-exclude-headers})81make_lib_descriptor(common INCLUDE_PRIVATE EXCLUDE_HEADERS ${mircommon-exclude-headers})
82make_lib_descriptor(cookie)
82make_lib_descriptor(platform INCLUDE_PRIVATE EXCLUDE_HEADERS ${mirplatform-exclude-headers})83make_lib_descriptor(platform INCLUDE_PRIVATE EXCLUDE_HEADERS ${mirplatform-exclude-headers})
83if(MIR_BUILD_PLATFORM_MESA_KMS)84if(MIR_BUILD_PLATFORM_MESA_KMS)
84make_lib_descriptor(clientplatformmesa LIBRARY_HEADER ${CMAKE_SOURCE_DIR}/src/include/client/mir/client_platform_factory.h)85make_lib_descriptor(clientplatformmesa LIBRARY_HEADER ${CMAKE_SOURCE_DIR}/src/include/client/mir/client_platform_factory.h)
@@ -124,7 +125,7 @@
124 )125 )
125endmacro(_define_abi_check_for)126endmacro(_define_abi_check_for)
126127
127set(the_libs mirserver mirclient mircommon mirplatform)128set(the_libs mirserver mirclient mircommon mirplatform mircookie)
128if(MIR_BUILD_PLATFORM_MESA_KMS)129if(MIR_BUILD_PLATFORM_MESA_KMS)
129 set(the_libs ${the_libs} mirclientplatformmesa mirplatformgraphicsmesakms)130 set(the_libs ${the_libs} mirclientplatformmesa mirplatformgraphicsmesakms)
130endif()131endif()
131132
=== modified file 'debian/control'
--- debian/control 2015-09-15 12:37:25 +0000
+++ debian/control 2015-09-17 09:09:28 +0000
@@ -43,6 +43,7 @@
43 uuid-dev,43 uuid-dev,
44 python3:any,44 python3:any,
45 dh-python,45 dh-python,
46 nettle-dev,
46Standards-Version: 3.9.447Standards-Version: 3.9.4
47Homepage: https://launchpad.net/mir48Homepage: https://launchpad.net/mir
48# If you aren't a member of ~mir-team but need to upload packaging changes,49# If you aren't a member of ~mir-team but need to upload packaging changes,
@@ -402,6 +403,33 @@
402 This package depends on a full set of graphics drivers for running Mir on top403 This package depends on a full set of graphics drivers for running Mir on top
403 of an existing Android driver stack.404 of an existing Android driver stack.
404405
406Package: libmircookie1
407Section: libs
408Architecture: any
409Multi-Arch: same
410Pre-Depends: ${misc:Pre-Depends}
411Depends: ${misc:Depends},
412Description: Produce and verify spoof-resistant timestamps - runtime library
413 libmircookie provides a simple mechanism for a group of cooperating processes
414 to hand out and verify difficult-to-forge timestamps to untrusted 3rd parties.
415 .
416 This package contains the runtime library for generating and verifying the
417 attestable timestamps.
418
419Package: libmircookie-dev
420Section: libdevel
421Architecture: any
422Multi-Arch: same
423Pre-Depends: ${misc:Pre-Depends}
424Depends: libmircookie1 (= ${binary:Version}),
425 ${misc:Depends},
426Description: Produce and verify spoof-resistant timestamps - development headers
427 libmircookie provides a simple mechanism for a group of cooperating processes
428 to hand out and verify difficult-to-forge timestamps to untrusted 3rd parties.
429 .
430 This package contains the development headers for building programs that
431 generate or verify the attestable timestamps.
432
405Package: python3-mir-perf-framework433Package: python3-mir-perf-framework
406Section: python434Section: python
407Architecture: all435Architecture: all
408436
=== added file 'debian/libmircookie-dev.install'
--- debian/libmircookie-dev.install 1970-01-01 00:00:00 +0000
+++ debian/libmircookie-dev.install 2015-09-17 09:09:28 +0000
@@ -0,0 +1,3 @@
1/usr/include/mircookie/
2/usr/lib/*/libmircookie.so
3/usr/lib/*/pkgconfig/mircookie.pc
04
=== added file 'debian/libmircookie1.install'
--- debian/libmircookie1.install 1970-01-01 00:00:00 +0000
+++ debian/libmircookie1.install 2015-09-17 09:09:28 +0000
@@ -0,0 +1,1 @@
1/usr/lib/*/libmircookie.so.1
02
=== modified file 'debian/mir-test-tools.install'
--- debian/mir-test-tools.install 2015-06-23 08:00:53 +0000
+++ debian/mir-test-tools.install 2015-09-17 09:09:28 +0000
@@ -10,3 +10,4 @@
10usr/lib/*/mir/server-platform/graphics-dummy.so10usr/lib/*/mir/server-platform/graphics-dummy.so
11usr/lib/*/mir/server-platform/input-stub.so11usr/lib/*/mir/server-platform/input-stub.so
12usr/lib/*/mir/client-platform/dummy.so12usr/lib/*/mir/client-platform/dummy.so
13usr/share/udev_recordings
1314
=== added directory 'include/cookie'
=== added directory 'include/cookie/mir'
=== added file 'include/cookie/mir/cookie_factory.h'
--- include/cookie/mir/cookie_factory.h 1970-01-01 00:00:00 +0000
+++ include/cookie/mir/cookie_factory.h 2015-09-17 09:09:28 +0000
@@ -0,0 +1,104 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#ifndef MIR_COOKIE_COOKIE_FACTORY_H_
20#define MIR_COOKIE_COOKIE_FACTORY_H_
21
22#include "mir_toolkit/cookie.h"
23
24#include <vector>
25#include <memory>
26
27namespace mir
28{
29namespace cookie
30{
31using Secret = std::vector<uint8_t>;
32
33/**
34 * \brief A source of moderately-difficult-to-spoof cookies.
35 *
36 * The primary motivation for this is to provide event timestamps that clients find it difficult to spoof.
37 * This is useful for focus grant and similar operations where shell behaviour should be dependent on
38 * the timestamp of the client event that caused the request.
39 *
40 * Some spoofing protection is desirable; experience with X clients shows that they will go to some effort
41 * to attempt to bypass focus stealing prevention.
42 *
43 */
44class CookieFactory
45{
46public:
47 /**
48 * Contruction function used to create a CookieFactory. The secret size must be
49 * no less then minimum_secret_size otherwise an expection will be thrown
50 *
51 * \param [in] secret A filled in secret used to set the key for the hash function
52 * \return A unique_ptr CookieFactory
53 */
54 static std::unique_ptr<CookieFactory> create_from_secret(Secret const& secret);
55
56 /**
57 * Contruction function used to create a CookieFactory as well as a secret.
58 * The secret size must be no less then minimum_secret_size otherwise an expection will be thrown
59 *
60 * \param [in] secret_size The size of the secret to create, must be larger then minimum_secret_size
61 * \param [out] save_secret The secret that was created.
62 * \return A unique_ptr CookieFactory
63 */
64 static std::unique_ptr<CookieFactory> create_saving_secret(Secret& save_secret,
65 unsigned secret_size = 2 * minimum_secret_size);
66
67 /**
68 * Contruction function used to create a CookieFactory and a secret which it keeps internally.
69 * The secret size must be no less then minimum_secret_size otherwise an expection will be thrown
70 *
71 * \param [in] secret_size The size of the secret to create, must be larger then minimum_secret_size
72 * \return A unique_ptr CookieFactory
73 */
74 static std::unique_ptr<CookieFactory> create_keeping_secret(unsigned secret_size = 2 * minimum_secret_size);
75
76 CookieFactory(CookieFactory const& factory) = delete;
77 CookieFactory& operator=(CookieFactory const& factory) = delete;
78 virtual ~CookieFactory() noexcept = default;
79
80 /**
81 * Turns a timestamp into a MAC and returns a MirCookie.
82 *
83 * \param [in] timestamp The timestamp
84 * \return MirCookie with the stored MAC and timestamp
85 */
86 virtual MirCookie timestamp_to_cookie(uint64_t const& timestamp) = 0;
87
88 /**
89 * Checks that a MirCookie is a valid MirCookie.
90 *
91 * \param [in] cookie A created MirCookie
92 * \return True when the MirCookie is valid, False when the MirCookie is not valid
93 */
94 virtual bool attest_timestamp(MirCookie const& cookie) = 0;
95
96 static unsigned const minimum_secret_size = 8;
97
98protected:
99 CookieFactory() = default;
100};
101
102}
103}
104#endif // MIR_COOKIE_COOKIE_FACTORY_H_
0105
=== added directory 'include/cookie/mir_toolkit'
=== added file 'include/cookie/mir_toolkit/cookie.h'
--- include/cookie/mir_toolkit/cookie.h 1970-01-01 00:00:00 +0000
+++ include/cookie/mir_toolkit/cookie.h 2015-09-17 09:09:28 +0000
@@ -0,0 +1,44 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#ifndef MIR_TOOLKIT_COOKIE_FACTORY_H_
20#define MIR_TOOLKIT_COOKIE_FACTORY_H_
21
22#include <stdint.h>
23
24/**
25 * \addtogroup mir_toolkit
26 * @{
27 */
28/* This is C code. Not C++. */
29#ifdef __cplusplus
30extern "C" {
31#endif
32
33typedef struct MirCookie
34{
35 uint64_t timestamp;
36 uint64_t mac;
37} MirCookie;
38
39#ifdef __cplusplus
40}
41#endif
42/**@}*/
43
44#endif // MIR_TOOLKIT_COOKIE_FACTORY_H_
045
=== modified file 'include/server/mir/server.h'
--- include/server/mir/server.h 2015-09-08 03:25:06 +0000
+++ include/server/mir/server.h 2015-09-17 09:09:28 +0000
@@ -34,6 +34,10 @@
34namespace input { class CompositeEventFilter; class InputDispatcher; class CursorListener; class TouchVisualizer; class InputDeviceHub;}34namespace input { class CompositeEventFilter; class InputDispatcher; class CursorListener; class TouchVisualizer; class InputDeviceHub;}
35namespace logging { class Logger; }35namespace logging { class Logger; }
36namespace options { class Option; }36namespace options { class Option; }
37namespace cookie
38{
39using Secret = std::vector<uint8_t>;
40}
37namespace shell41namespace shell
38{42{
39class DisplayLayout;43class DisplayLayout;
@@ -79,6 +83,13 @@
79 /// This must remain valid while apply_settings() and run() are called.83 /// This must remain valid while apply_settings() and run() are called.
80 void set_command_line(int argc, char const* argv[]);84 void set_command_line(int argc, char const* argv[]);
8185
86 /// creates the CookieFactory from the given secret
87 /// This secret is used to generate timestamps that can be attested to by
88 /// libmircookie. Any process this secret is shared with can verify Mir-generated
89 /// cookies, or produce their own.
90 /// \note If not explicitly set, a random secret will be chosen.
91 void override_the_cookie_factory(mir::cookie::Secret const& secret);
92
82 /// Applies any configuration options, hooks, or custom implementations.93 /// Applies any configuration options, hooks, or custom implementations.
83 /// Must be called before calling run() or accessing any mir subsystems.94 /// Must be called before calling run() or accessing any mir subsystems.
84 void apply_settings();95 void apply_settings();
@@ -381,6 +392,7 @@
381 /// using the format "fd://%d".392 /// using the format "fd://%d".
382 auto open_prompt_socket() -> Fd;393 auto open_prompt_socket() -> Fd;
383/** @} */394/** @} */
395
384private:396private:
385 struct ServerConfiguration;397 struct ServerConfiguration;
386 struct Self;398 struct Self;
387399
=== modified file 'include/test/mir_test_framework/executable_path.h'
--- include/test/mir_test_framework/executable_path.h 2015-06-22 03:04:56 +0000
+++ include/test/mir_test_framework/executable_path.h 2015-09-17 09:09:28 +0000
@@ -26,6 +26,7 @@
26std::string executable_path();26std::string executable_path();
2727
28std::string library_path();28std::string library_path();
29std::string udev_recordings_path();
29std::string server_platform(std::string const& name);30std::string server_platform(std::string const& name);
30std::string client_platform(std::string const& name);31std::string client_platform(std::string const& name);
31}32}
3233
=== modified file 'src/CMakeLists.txt'
--- src/CMakeLists.txt 2015-09-07 11:54:56 +0000
+++ src/CMakeLists.txt 2015-09-17 09:09:28 +0000
@@ -1,6 +1,9 @@
1# We need MIRPLATFORM_ABI in both libmirplatform and the platform implementations.1# We need MIRPLATFORM_ABI in both libmirplatform and the platform implementations.
2set(MIRPLATFORM_ABI 10)2set(MIRPLATFORM_ABI 10)
33
4# Add the cookie implementation before exposing any APIs
5add_subdirectory(cookie/)
6
4# We need MIR_CLIENT_PLATFORM_PATH in both libmirclient and the platform7# We need MIR_CLIENT_PLATFORM_PATH in both libmirclient and the platform
5# implementations8# implementations
6set(MIR_CLIENT_PLATFORM_PATH9set(MIR_CLIENT_PLATFORM_PATH
710
=== added directory 'src/cookie'
=== added file 'src/cookie/CMakeLists.txt'
--- src/cookie/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ src/cookie/CMakeLists.txt 2015-09-17 09:09:28 +0000
@@ -0,0 +1,48 @@
1set(PREFIX "${CMAKE_INSTALL_PREFIX}")
2set(EXEC_PREFIX "${CMAKE_INSTALL_PREFIX}")
3set(LIBDIR "${CMAKE_INSTALL_FULL_LIBDIR}")
4set(INCLUDEDIR "${CMAKE_INSTALL_PREFIX}/include")
5
6configure_file(
7 ${CMAKE_CURRENT_SOURCE_DIR}/mircookie.pc.in
8 ${CMAKE_CURRENT_BINARY_DIR}/mircookie.pc
9 @ONLY
10)
11
12include_directories(
13 ${PROJECT_SOURCE_DIR}/include/cookie
14 ${NETTLE_INCLUDE_DIRS}
15)
16
17set(MIRCOOKIE_ABI 1)
18set(symbol_map ${CMAKE_SOURCE_DIR}/src/cookie/symbols.map)
19
20add_library(mircookie SHARED
21
22 cookie_factory.cpp
23)
24
25set_target_properties(mircookie
26 PROPERTIES
27 SOVERSION ${MIRCOOKIE_ABI}
28 LINK_FLAGS "-Wl,--exclude-libs=ALL -Wl,--version-script,${symbol_map}"
29)
30
31target_link_libraries(mircookie
32 ${NETTLE_LDFLAGS} ${NETTLE_LIBS}
33)
34
35install(
36 TARGETS mircookie
37 LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
38 ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR})
39
40install(
41 DIRECTORY ${CMAKE_SOURCE_DIR}/include/cookie/mir
42 DESTINATION "include/mircookie"
43)
44
45install(
46 FILES ${CMAKE_CURRENT_BINARY_DIR}/mircookie.pc
47 DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig
48)
049
=== added file 'src/cookie/cookie_factory.cpp'
--- src/cookie/cookie_factory.cpp 1970-01-01 00:00:00 +0000
+++ src/cookie/cookie_factory.cpp 2015-09-17 09:09:28 +0000
@@ -0,0 +1,161 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#include "mir/cookie_factory.h"
20
21#include <algorithm>
22#include <random>
23#include <memory>
24#include <system_error>
25
26#include <nettle/hmac.h>
27#include <linux/random.h>
28#include <sys/types.h>
29#include <sys/stat.h>
30#include <fcntl.h>
31#include <sys/select.h>
32
33#include <boost/throw_exception.hpp>
34
35namespace
36{
37std::string const random_device_path{"/dev/random"};
38std::string const urandom_device_path{"/dev/urandom"};
39int const wait_seconds{30};
40}
41
42static mir::cookie::Secret get_random_data(unsigned size)
43{
44 mir::cookie::Secret buffer(size);
45 int random_fd;
46 int retval;
47 fd_set rfds;
48
49 struct timeval tv;
50 tv.tv_sec = wait_seconds;
51 tv.tv_usec = 0;
52
53 if ((random_fd = open(random_device_path.c_str(), O_RDONLY)) == -1)
54 {
55 int error = errno;
56 BOOST_THROW_EXCEPTION(std::system_error(error, std::system_category(),
57 "open failed on device " + random_device_path));
58 }
59
60 FD_ZERO(&rfds);
61 FD_SET(random_fd, &rfds);
62
63 /* We want to block until *some* entropy exists on boot, then use urandom once we have some */
64 retval = select(random_fd + 1, &rfds, NULL, NULL, &tv);
65
66 /* We are done with /dev/random at this point, and it is either an error or ready to be read */
67 if (close(random_fd) == -1)
68 {
69 int error = errno;
70 BOOST_THROW_EXCEPTION(std::system_error(error, std::system_category(),
71 "close failed on device " + random_device_path));
72 }
73
74 if (retval == -1)
75 {
76 int error = errno;
77 BOOST_THROW_EXCEPTION(std::system_error(error, std::system_category(),
78 "select failed on file descriptor " + std::to_string(random_fd) +
79 " from device " + random_device_path));
80 }
81 else if (retval && FD_ISSET(random_fd, &rfds))
82 {
83 std::uniform_int_distribution<uint8_t> dist;
84 std::random_device rand_dev(urandom_device_path);
85
86 std::generate(std::begin(buffer), std::end(buffer), [&]() {
87 return dist(rand_dev);
88 });
89 }
90 else
91 {
92 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to read from device: " + random_device_path +
93 " after: " + std::to_string(wait_seconds) + " seconds"));
94 }
95
96 return buffer;
97}
98
99class CookieFactoryNettle : public mir::cookie::CookieFactory
100{
101public:
102 CookieFactoryNettle(mir::cookie::Secret const& secret)
103 {
104 if (secret.size() < minimum_secret_size)
105 BOOST_THROW_EXCEPTION(std::logic_error("Secret size " + std::to_string(secret.size()) + " is to small, require " +
106 std::to_string(minimum_secret_size) + " or greater."));
107
108 hmac_sha1_set_key(&ctx, secret.size(), secret.data());
109 }
110
111 virtual ~CookieFactoryNettle() noexcept = default;
112
113 MirCookie timestamp_to_cookie(uint64_t const& timestamp) override
114 {
115 MirCookie cookie { timestamp, 0 };
116 calculate_mac(cookie);
117 return cookie;
118 }
119
120 bool attest_timestamp(MirCookie const& cookie) override
121 {
122 return verify_mac(cookie);
123 }
124
125private:
126 void calculate_mac(MirCookie& cookie)
127 {
128 hmac_sha1_update(&ctx, sizeof(cookie.timestamp), reinterpret_cast<uint8_t*>(&cookie.timestamp));
129 hmac_sha1_digest(&ctx, sizeof(cookie.mac), reinterpret_cast<uint8_t*>(&cookie.mac));
130 }
131
132 bool verify_mac(MirCookie const& cookie)
133 {
134 decltype(cookie.mac) calculated_mac;
135 uint8_t* message = reinterpret_cast<uint8_t*>(const_cast<decltype(cookie.timestamp)*>(&cookie.timestamp));
136 hmac_sha1_update(&ctx, sizeof(cookie.timestamp), message);
137 hmac_sha1_digest(&ctx, sizeof(calculated_mac), reinterpret_cast<uint8_t*>(&calculated_mac));
138
139 return calculated_mac == cookie.mac;
140 }
141
142 struct hmac_sha1_ctx ctx;
143};
144
145std::unique_ptr<mir::cookie::CookieFactory> mir::cookie::CookieFactory::create_from_secret(mir::cookie::Secret const& secret)
146{
147 return std::make_unique<CookieFactoryNettle>(secret);
148}
149
150std::unique_ptr<mir::cookie::CookieFactory> mir::cookie::CookieFactory::create_saving_secret(mir::cookie::Secret& save_secret,
151 unsigned secret_size)
152{
153 save_secret = get_random_data(secret_size);
154 return std::make_unique<CookieFactoryNettle>(save_secret);
155}
156
157std::unique_ptr<mir::cookie::CookieFactory> mir::cookie::CookieFactory::create_keeping_secret(unsigned secret_size)
158{
159 auto secret = get_random_data(secret_size);
160 return std::make_unique<CookieFactoryNettle>(secret);
161}
0162
=== added file 'src/cookie/mircookie.pc.in'
--- src/cookie/mircookie.pc.in 1970-01-01 00:00:00 +0000
+++ src/cookie/mircookie.pc.in 2015-09-17 09:09:28 +0000
@@ -0,0 +1,11 @@
1prefix=@PREFIX@
2exec_prefix=@EXEC_PREFIX@
3libdir=@LIBDIR@
4includedir=@INCLUDEDIR@/mircookie
5
6Name: mircookie
7Description: Mir client library
8Version: @MIR_VERSION@
9Requires.private: nettle
10Libs: -L${libdir} -lmircookie
11Cflags: -I${includedir}
012
=== added file 'src/cookie/symbols.map'
--- src/cookie/symbols.map 1970-01-01 00:00:00 +0000
+++ src/cookie/symbols.map 2015-09-17 09:09:28 +0000
@@ -0,0 +1,9 @@
1MIR_COOKIE_1 {
2 global:
3 extern "C++" {
4 mir::cookie::CookieFactory::create_from_secret*;
5 mir::cookie::CookieFactory::create_saving_secret*;
6 mir::cookie::CookieFactory::create_keeping_secret*;
7 };
8 local: *;
9};
010
=== modified file 'src/include/server/mir/default_server_configuration.h'
--- src/include/server/mir/default_server_configuration.h 2015-09-08 03:25:06 +0000
+++ src/include/server/mir/default_server_configuration.h 2015-09-17 09:09:28 +0000
@@ -41,6 +41,10 @@
41class SharedLibrary;41class SharedLibrary;
42class SharedLibraryProberReport;42class SharedLibraryProberReport;
4343
44namespace cookie
45{
46class CookieFactory;
47}
44namespace dispatch48namespace dispatch
45{49{
46class MultiplexingDispatchable;50class MultiplexingDispatchable;
@@ -186,7 +190,8 @@
186 std::shared_ptr<DisplayChanger> the_display_changer() override;190 std::shared_ptr<DisplayChanger> the_display_changer() override;
187 std::shared_ptr<graphics::Platform> the_graphics_platform() override;191 std::shared_ptr<graphics::Platform> the_graphics_platform() override;
188 std::shared_ptr<input::InputDispatcher> the_input_dispatcher() override;192 std::shared_ptr<input::InputDispatcher> the_input_dispatcher() override;
189 std::shared_ptr<EmergencyCleanup> the_emergency_cleanup() override;193 std::shared_ptr<EmergencyCleanup> the_emergency_cleanup() override;
194 std::shared_ptr<cookie::CookieFactory> the_cookie_factory() override;
190 /**195 /**
191 * Function to call when a "fatal" error occurs. This implementation allows196 * Function to call when a "fatal" error occurs. This implementation allows
192 * the default strategy to be overridden by --on-fatal-error-abort to force a197 * the default strategy to be overridden by --on-fatal-error-abort to force a
@@ -441,6 +446,7 @@
441 CachedPtr<SharedLibraryProberReport> shared_library_prober_report;446 CachedPtr<SharedLibraryProberReport> shared_library_prober_report;
442 CachedPtr<shell::Shell> shell;447 CachedPtr<shell::Shell> shell;
443 CachedPtr<scene::ApplicationNotRespondingDetector> application_not_responding_detector;448 CachedPtr<scene::ApplicationNotRespondingDetector> application_not_responding_detector;
449 CachedPtr<cookie::CookieFactory> cookie_factory;
444450
445private:451private:
446 std::shared_ptr<options::Configuration> const configuration_options;452 std::shared_ptr<options::Configuration> const configuration_options;
447453
=== modified file 'src/include/server/mir/server_configuration.h'
--- src/include/server/mir/server_configuration.h 2015-06-18 04:38:28 +0000
+++ src/include/server/mir/server_configuration.h 2015-09-17 09:09:28 +0000
@@ -22,6 +22,10 @@
2222
23namespace mir23namespace mir
24{24{
25namespace cookie
26{
27class CookieFactory;
28}
25namespace compositor29namespace compositor
26{30{
27class Compositor;31class Compositor;
@@ -75,6 +79,7 @@
75 virtual std::shared_ptr<DisplayChanger> the_display_changer() = 0;79 virtual std::shared_ptr<DisplayChanger> the_display_changer() = 0;
76 virtual std::shared_ptr<graphics::Platform> the_graphics_platform() = 0;80 virtual std::shared_ptr<graphics::Platform> the_graphics_platform() = 0;
77 virtual std::shared_ptr<EmergencyCleanup> the_emergency_cleanup() = 0;81 virtual std::shared_ptr<EmergencyCleanup> the_emergency_cleanup() = 0;
82 virtual std::shared_ptr<cookie::CookieFactory> the_cookie_factory() = 0;
78 virtual auto the_fatal_error_strategy() -> void (*)(char const* reason, ...) = 0;83 virtual auto the_fatal_error_strategy() -> void (*)(char const* reason, ...) = 0;
79 virtual std::shared_ptr<scene::ApplicationNotRespondingDetector> the_application_not_responding_detector() = 0;84 virtual std::shared_ptr<scene::ApplicationNotRespondingDetector> the_application_not_responding_detector() = 0;
8085
8186
=== modified file 'src/server/CMakeLists.txt'
--- src/server/CMakeLists.txt 2015-09-07 11:54:56 +0000
+++ src/server/CMakeLists.txt 2015-09-17 09:09:28 +0000
@@ -10,6 +10,7 @@
10 ${PROJECT_SOURCE_DIR}/src/include/platform10 ${PROJECT_SOURCE_DIR}/src/include/platform
11 ${PROJECT_SOURCE_DIR}/src/include/client11 ${PROJECT_SOURCE_DIR}/src/include/client
12 ${PROJECT_SOURCE_DIR}/src/include/server12 ${PROJECT_SOURCE_DIR}/src/include/server
13 ${PROJECT_SOURCE_DIR}/include/cookie
13 ${GLIB_INCLUDE_DIRS}14 ${GLIB_INCLUDE_DIRS}
14)15)
15add_definitions(-DMIR_SERVER_INPUT_PLATFORM_VERSION="${MIR_SERVER_INPUT_PLATFORM_VERSION}")16add_definitions(-DMIR_SERVER_INPUT_PLATFORM_VERSION="${MIR_SERVER_INPUT_PLATFORM_VERSION}")
@@ -95,7 +96,7 @@
95 mirplatform96 mirplatform
96 mircommon97 mircommon
97 mirprotobuf98 mirprotobuf
9899 mircookie
99 xcursorloader-static100 xcursorloader-static
100101
101 ${GLog_LIBRARY}102 ${GLog_LIBRARY}
102103
=== modified file 'src/server/default_server_configuration.cpp'
--- src/server/default_server_configuration.cpp 2015-06-17 05:20:42 +0000
+++ src/server/default_server_configuration.cpp 2015-09-17 09:09:28 +0000
@@ -1,5 +1,5 @@
1/*1/*
2 * Copyright © 2012-2014 Canonical Ltd.2 * Copyright © 2012-2015 Canonical Ltd.
3 *3 *
4 * This program is free software: you can redistribute it and/or modify it4 * This program is free software: you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 3,5 * under the terms of the GNU General Public License version 3,
@@ -24,6 +24,7 @@
24#include "mir/default_server_status_listener.h"24#include "mir/default_server_status_listener.h"
25#include "mir/emergency_cleanup.h"25#include "mir/emergency_cleanup.h"
26#include "mir/default_configuration.h"26#include "mir/default_configuration.h"
27#include "mir/cookie_factory.h"
2728
28#include "mir/logging/dumb_console_logger.h"29#include "mir/logging/dumb_console_logger.h"
29#include "mir/options/program_option.h"30#include "mir/options/program_option.h"
@@ -41,6 +42,8 @@
41#include "mir/scene/null_prompt_session_listener.h"42#include "mir/scene/null_prompt_session_listener.h"
42#include "default_emergency_cleanup.h"43#include "default_emergency_cleanup.h"
4344
45#include <type_traits>
46
44namespace mc = mir::compositor;47namespace mc = mir::compositor;
45namespace geom = mir::geometry;48namespace geom = mir::geometry;
46namespace mf = mir::frontend;49namespace mf = mir::frontend;
@@ -51,6 +54,11 @@
51namespace msh = mir::shell;54namespace msh = mir::shell;
52namespace mi = mir::input;55namespace mi = mir::input;
5356
57namespace
58{
59 unsigned const secret_size{64};
60}
61
54mir::DefaultServerConfiguration::DefaultServerConfiguration(int argc, char const* argv[]) :62mir::DefaultServerConfiguration::DefaultServerConfiguration(int argc, char const* argv[]) :
55 DefaultServerConfiguration(std::make_shared<mo::DefaultConfiguration>(argc, argv))63 DefaultServerConfiguration(std::make_shared<mo::DefaultConfiguration>(argc, argv))
56{64{
@@ -173,6 +181,18 @@
173 });181 });
174}182}
175183
184std::shared_ptr<mir::cookie::CookieFactory> mir::DefaultServerConfiguration::the_cookie_factory()
185{
186 return cookie_factory(
187 []()
188 {
189 static_assert(secret_size >= mir::cookie::CookieFactory::minimum_secret_size,
190 "Secret size is smaller then the minimum secret size");
191
192 return mir::cookie::CookieFactory::create_keeping_secret(secret_size);
193 });
194}
195
176auto mir::DefaultServerConfiguration::the_fatal_error_strategy()196auto mir::DefaultServerConfiguration::the_fatal_error_strategy()
177-> void (*)(char const* reason, ...)197-> void (*)(char const* reason, ...)
178{198{
179199
=== modified file 'src/server/server.cpp'
--- src/server/server.cpp 2015-09-08 03:25:06 +0000
+++ src/server/server.cpp 2015-09-17 09:09:28 +0000
@@ -29,6 +29,7 @@
29#include "mir/main_loop.h"29#include "mir/main_loop.h"
30#include "mir/report_exception.h"30#include "mir/report_exception.h"
31#include "mir/run_mir.h"31#include "mir/run_mir.h"
32#include "mir/cookie_factory.h"
3233
33// TODO these are used to frig a stub renderer when running headless34// TODO these are used to frig a stub renderer when running headless
34#include "mir/compositor/renderer.h"35#include "mir/compositor/renderer.h"
@@ -101,6 +102,7 @@
101 std::weak_ptr<options::Option> options;102 std::weak_ptr<options::Option> options;
102 std::string config_file;103 std::string config_file;
103 std::shared_ptr<ServerConfiguration> server_config;104 std::shared_ptr<ServerConfiguration> server_config;
105 std::shared_ptr<cookie::CookieFactory> cookie_factory;
104106
105 std::function<void()> init_callback{[]{}};107 std::function<void()> init_callback{[]{}};
106 int argc{0};108 int argc{0};
@@ -207,6 +209,15 @@
207 return mir::DefaultServerConfiguration::the_renderer_factory();209 return mir::DefaultServerConfiguration::the_renderer_factory();
208 }210 }
209211
212 auto the_cookie_factory() -> std::shared_ptr<cookie::CookieFactory> override
213 {
214 if (self->cookie_factory)
215 {
216 return self->cookie_factory;
217 }
218 return mir::DefaultServerConfiguration::the_cookie_factory();
219 }
220
210 using mir::DefaultServerConfiguration::the_options;221 using mir::DefaultServerConfiguration::the_options;
211222
212 FOREACH_OVERRIDE(MIR_SERVER_CONFIG_OVERRIDE)223 FOREACH_OVERRIDE(MIR_SERVER_CONFIG_OVERRIDE)
@@ -277,6 +288,12 @@
277 self->argv = argv;288 self->argv = argv;
278}289}
279290
291void mir::Server::override_the_cookie_factory(mir::cookie::Secret const& secret)
292{
293 verify_setting_allowed(self->server_config);
294 self->cookie_factory = mir::cookie::CookieFactory::create_from_secret(secret);
295}
296
280void mir::Server::add_init_callback(std::function<void()> const& init_callback)297void mir::Server::add_init_callback(std::function<void()> const& init_callback)
281{298{
282 auto const& existing = self->init_callback;299 auto const& existing = self->init_callback;
283300
=== modified file 'src/server/symbols.map'
--- src/server/symbols.map 2015-09-11 12:29:40 +0000
+++ src/server/symbols.map 2015-09-17 09:09:28 +0000
@@ -135,6 +135,7 @@
135 mir::ServerActionQueue::?ServerActionQueue*;135 mir::ServerActionQueue::?ServerActionQueue*;
136 mir::ServerActionQueue::ServerActionQueue*;136 mir::ServerActionQueue::ServerActionQueue*;
137 mir::Server::add_configuration_option*;137 mir::Server::add_configuration_option*;
138 mir::Server::override_the_cookie_factory*;
138 mir::Server::add_emergency_cleanup*;139 mir::Server::add_emergency_cleanup*;
139 mir::Server::add_init_callback*;140 mir::Server::add_init_callback*;
140 mir::Server::apply_settings*;141 mir::Server::apply_settings*;
@@ -590,6 +591,7 @@
590 mir::DefaultServerConfiguration::the_buffer_stream_factory*;591 mir::DefaultServerConfiguration::the_buffer_stream_factory*;
591 mir::DefaultServerConfiguration::the_clock*;592 mir::DefaultServerConfiguration::the_clock*;
592 mir::DefaultServerConfiguration::the_composite_event_filter*;593 mir::DefaultServerConfiguration::the_composite_event_filter*;
594 mir::DefaultServerConfiguration::the_cookie_factory*;
593 mir::DefaultServerConfiguration::the_event_filter_chain_dispatcher*;595 mir::DefaultServerConfiguration::the_event_filter_chain_dispatcher*;
594 mir::DefaultServerConfiguration::the_surface_input_dispatcher;596 mir::DefaultServerConfiguration::the_surface_input_dispatcher;
595 mir::DefaultServerConfiguration::the_compositor*;597 mir::DefaultServerConfiguration::the_compositor*;
596598
=== modified file 'tests/acceptance-tests/CMakeLists.txt'
--- tests/acceptance-tests/CMakeLists.txt 2015-09-09 18:59:38 +0000
+++ tests/acceptance-tests/CMakeLists.txt 2015-09-17 09:09:28 +0000
@@ -2,6 +2,8 @@
22
3include_directories(3include_directories(
4 ${CMAKE_SOURCE_DIR}4 ${CMAKE_SOURCE_DIR}
5 ${PROJECT_SOURCE_DIR}/include/cookie
6 ${UMOCKDEV_INCLUDE_DIRS}
5)7)
68
7set(9set(
@@ -80,6 +82,7 @@
80 mirclient-debug-extension82 mirclient-debug-extension
81 mirserver83 mirserver
8284
85 ${UMOCKDEV_LIBRARIES}
83 ${CMAKE_THREAD_LIBS_INIT} # Link in pthread.86 ${CMAKE_THREAD_LIBS_INIT} # Link in pthread.
84)87)
8588
8689
=== modified file 'tests/include/mir_test_framework/udev_environment.h'
--- tests/include/mir_test_framework/udev_environment.h 2015-02-22 07:46:25 +0000
+++ tests/include/mir_test_framework/udev_environment.h 2015-09-17 09:09:28 +0000
@@ -53,6 +53,32 @@
53 */53 */
54 void add_standard_device(std::string const& name);54 void add_standard_device(std::string const& name);
5555
56 /**
57 * Load an ioctl recording for a UMockdev device
58 *
59 * Looks for a <tt>name</tt>.ioctl file recorded with umockdev-record --ioctl
60 * and adds it to the associated device in the testbed.
61 *
62 * The udev records for the device these ioctl records will be associated with
63 * must already exist in the testbed
64 *
65 * @param name The unadorned filename for the ioctl records to add
66 */
67 void load_device_ioctls(std::string const& name);
68
69 /**
70 * Load an evemu evdev recording for a UMockdev device
71 *
72 * Looks for a <tt>name</tt>.evemu file recorded with umockdev-record --evemu
73 * (or evemu-record) and associates it with the udev device it was recorded from.
74 *
75 * The udev records for the device this recording is associated with
76 * must already exist in the testbed
77 *
78 * @param name The unadorned filename for the ioctl records to add
79 */
80 void load_device_evemu(std::string const& name);
81
56 UMockdevTestbed *testbed;82 UMockdevTestbed *testbed;
57 std::string const recordings_path;83 std::string const recordings_path;
58};84};
5985
=== modified file 'tests/integration-tests/CMakeLists.txt'
--- tests/integration-tests/CMakeLists.txt 2015-09-09 18:59:38 +0000
+++ tests/integration-tests/CMakeLists.txt 2015-09-17 09:09:28 +0000
@@ -4,6 +4,7 @@
4 ${CMAKE_SOURCE_DIR}4 ${CMAKE_SOURCE_DIR}
5 ${PROTOBUF_INCLUDE_DIRS}5 ${PROTOBUF_INCLUDE_DIRS}
6 ${CMAKE_CURRENT_BINARY_DIR}6 ${CMAKE_CURRENT_BINARY_DIR}
7 ${PROJECT_SOURCE_DIR}/include/cookie
7 ${PROJECT_SOURCE_DIR}/src/include/platform8 ${PROJECT_SOURCE_DIR}/src/include/platform
8 ${PROJECT_SOURCE_DIR}/src/include/common9 ${PROJECT_SOURCE_DIR}/src/include/common
9 ${PROJECT_SOURCE_DIR}/src/include/server10 ${PROJECT_SOURCE_DIR}/src/include/server
1011
=== modified file 'tests/mir_test_framework/CMakeLists.txt'
--- tests/mir_test_framework/CMakeLists.txt 2015-09-14 17:04:48 +0000
+++ tests/mir_test_framework/CMakeLists.txt 2015-09-17 09:09:28 +0000
@@ -16,6 +16,8 @@
16 -DMIR_SERVER_PLATFORM_PATH="${MIR_SERVER_PLATFORM_PATH}"16 -DMIR_SERVER_PLATFORM_PATH="${MIR_SERVER_PLATFORM_PATH}"
17 -DMIR_CLIENT_PLATFORM_ABI_STRING="${MIR_CLIENT_PLATFORM_ABI}"17 -DMIR_CLIENT_PLATFORM_ABI_STRING="${MIR_CLIENT_PLATFORM_ABI}"
18 -DMIR_SERVER_GRAPHICS_PLATFORM_ABI_STRING="${MIR_SERVER_GRAPHICS_PLATFORM_ABI}"18 -DMIR_SERVER_GRAPHICS_PLATFORM_ABI_STRING="${MIR_SERVER_GRAPHICS_PLATFORM_ABI}"
19 -DMIR_INSTALL_PREFIX="${CMAKE_INSTALL_PREFIX}"
20 -DMIR_BUILD_PREFIX="${CMAKE_BINARY_DIR}"
19 )21 )
2022
21add_library(mir-public-test-framework OBJECT23add_library(mir-public-test-framework OBJECT
@@ -161,3 +163,5 @@
161install(TARGETS mirplatformgraphicsstub LIBRARY DESTINATION ${MIR_SERVER_PLATFORM_PATH})163install(TARGETS mirplatformgraphicsstub LIBRARY DESTINATION ${MIR_SERVER_PLATFORM_PATH})
162164
163install(TARGETS mirclientplatformstub LIBRARY DESTINATION ${MIR_CLIENT_PLATFORM_PATH})165install(TARGETS mirclientplatformstub LIBRARY DESTINATION ${MIR_CLIENT_PLATFORM_PATH})
166
167add_subdirectory(udev_recordings/)
164168
=== modified file 'tests/mir_test_framework/executable_path.cpp'
--- tests/mir_test_framework/executable_path.cpp 2015-06-17 05:20:42 +0000
+++ tests/mir_test_framework/executable_path.cpp 2015-09-17 09:09:28 +0000
@@ -45,6 +45,19 @@
45 return executable_path() + "/../lib";45 return executable_path() + "/../lib";
46}46}
4747
48std::string mir_test_framework::udev_recordings_path()
49{
50 std::string run_path = executable_path() + "/udev_recordings";
51 std::string install_path = MIR_INSTALL_PREFIX"/share/udev_recordings";
52
53 if (boost::filesystem::exists(run_path))
54 return run_path;
55 else if (boost::filesystem::exists(install_path))
56 return install_path;
57
58 BOOST_THROW_EXCEPTION(std::runtime_error("Failed to find udev_recordings in standard search locations"));
59}
60
48std::string mir_test_framework::server_platform(std::string const& name)61std::string mir_test_framework::server_platform(std::string const& name)
49{62{
50 std::string libname{name};63 std::string libname{name};
5164
=== modified file 'tests/mir_test_framework/udev_environment.cpp'
--- tests/mir_test_framework/udev_environment.cpp 2015-02-22 07:46:25 +0000
+++ tests/mir_test_framework/udev_environment.cpp 2015-09-17 09:09:28 +0000
@@ -38,7 +38,7 @@
38namespace mtf = mir_test_framework;38namespace mtf = mir_test_framework;
3939
40mtf::UdevEnvironment::UdevEnvironment()40mtf::UdevEnvironment::UdevEnvironment()
41 : recordings_path(mtf::executable_path() + "/udev_recordings")41 : recordings_path(mtf::udev_recordings_path())
42{42{
43 testbed = umockdev_testbed_new();43 testbed = umockdev_testbed_new();
44}44}
@@ -109,4 +109,41 @@
109 }109 }
110 }110 }
111 }111 }
112
113 auto script_filename = recordings_path + "/" + name + ".script";
114 if (stat(script_filename.c_str(), &sb) == 0)
115 {
116 if (S_ISREG(sb.st_mode) || S_ISLNK(sb.st_mode))
117 {
118 if (!umockdev_testbed_load_script(testbed, NULL, script_filename.c_str(), &err))
119 {
120 BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Failed to load device recording: ") +
121 err->message));
122 }
123 }
124 }
125}
126
127void mtf::UdevEnvironment::load_device_ioctls(std::string const& name)
128{
129 auto ioctls_filename = recordings_path + "/" + name + ".ioctl";
130
131 GError* err = nullptr;
132 if (!umockdev_testbed_load_ioctl(testbed, NULL, ioctls_filename.c_str(), &err))
133 {
134 BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Failed to load ioctl recording: ") +
135 err->message));
136 }
137}
138
139void mtf::UdevEnvironment::load_device_evemu(std::string const& name)
140{
141 auto evemu_filename = recordings_path + "/" + name + ".evemu";
142
143 GError* err = nullptr;
144 if (!umockdev_testbed_load_evemu_events(testbed, NULL, evemu_filename.c_str(), &err))
145 {
146 BOOST_THROW_EXCEPTION(std::runtime_error(std::string("Failed to load evemu recording: ") +
147 err->message));
148 }
112}149}
113150
=== added file 'tests/mir_test_framework/udev_recordings/CMakeLists.txt'
--- tests/mir_test_framework/udev_recordings/CMakeLists.txt 1970-01-01 00:00:00 +0000
+++ tests/mir_test_framework/udev_recordings/CMakeLists.txt 2015-09-17 09:09:28 +0000
@@ -0,0 +1,3 @@
1file(GLOB UDEV_FILES *.umockdev *.ioctl *.evemu)
2
3install(FILES ${UDEV_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/share/udev_recordings)
04
=== added file 'tests/mir_test_framework/udev_recordings/laptop-keyboard-hello.evemu'
--- tests/mir_test_framework/udev_recordings/laptop-keyboard-hello.evemu 1970-01-01 00:00:00 +0000
+++ tests/mir_test_framework/udev_recordings/laptop-keyboard-hello.evemu 2015-09-17 09:09:28 +0000
@@ -0,0 +1,272 @@
1# device /dev/input/event4
2# EVEMU 1.2
3# Input device name: "AT Translated Set 2 keyboard"
4# Input device ID: bus 0x11 vendor 0x01 product 0x01 version 0xab83
5# Supported events:
6# Event type 0 (EV_SYN)
7# Event code 0 (SYN_REPORT)
8# Event code 1 (SYN_CONFIG)
9# Event code 4 (FF_STATUS_STOPPED)
10# Event code 17 ((null))
11# Event code 20 ((null))
12# Event type 1 (EV_KEY)
13# Event code 1 (KEY_ESC)
14# Event code 2 (KEY_1)
15# Event code 3 (KEY_2)
16# Event code 4 (KEY_3)
17# Event code 5 (KEY_4)
18# Event code 6 (KEY_5)
19# Event code 7 (KEY_6)
20# Event code 8 (KEY_7)
21# Event code 9 (KEY_8)
22# Event code 10 (KEY_9)
23# Event code 11 (KEY_0)
24# Event code 12 (KEY_MINUS)
25# Event code 13 (KEY_EQUAL)
26# Event code 14 (KEY_BACKSPACE)
27# Event code 15 (KEY_TAB)
28# Event code 16 (KEY_Q)
29# Event code 17 (KEY_W)
30# Event code 18 (KEY_E)
31# Event code 19 (KEY_R)
32# Event code 20 (KEY_T)
33# Event code 21 (KEY_Y)
34# Event code 22 (KEY_U)
35# Event code 23 (KEY_I)
36# Event code 24 (KEY_O)
37# Event code 25 (KEY_P)
38# Event code 26 (KEY_LEFTBRACE)
39# Event code 27 (KEY_RIGHTBRACE)
40# Event code 28 (KEY_ENTER)
41# Event code 29 (KEY_LEFTCTRL)
42# Event code 30 (KEY_A)
43# Event code 31 (KEY_S)
44# Event code 32 (KEY_D)
45# Event code 33 (KEY_F)
46# Event code 34 (KEY_G)
47# Event code 35 (KEY_H)
48# Event code 36 (KEY_J)
49# Event code 37 (KEY_K)
50# Event code 38 (KEY_L)
51# Event code 39 (KEY_SEMICOLON)
52# Event code 40 (KEY_APOSTROPHE)
53# Event code 41 (KEY_GRAVE)
54# Event code 42 (KEY_LEFTSHIFT)
55# Event code 43 (KEY_BACKSLASH)
56# Event code 44 (KEY_Z)
57# Event code 45 (KEY_X)
58# Event code 46 (KEY_C)
59# Event code 47 (KEY_V)
60# Event code 48 (KEY_B)
61# Event code 49 (KEY_N)
62# Event code 50 (KEY_M)
63# Event code 51 (KEY_COMMA)
64# Event code 52 (KEY_DOT)
65# Event code 53 (KEY_SLASH)
66# Event code 54 (KEY_RIGHTSHIFT)
67# Event code 55 (KEY_KPASTERISK)
68# Event code 56 (KEY_LEFTALT)
69# Event code 57 (KEY_SPACE)
70# Event code 58 (KEY_CAPSLOCK)
71# Event code 59 (KEY_F1)
72# Event code 60 (KEY_F2)
73# Event code 61 (KEY_F3)
74# Event code 62 (KEY_F4)
75# Event code 63 (KEY_F5)
76# Event code 64 (KEY_F6)
77# Event code 65 (KEY_F7)
78# Event code 66 (KEY_F8)
79# Event code 67 (KEY_F9)
80# Event code 68 (KEY_F10)
81# Event code 69 (KEY_NUMLOCK)
82# Event code 70 (KEY_SCROLLLOCK)
83# Event code 71 (KEY_KP7)
84# Event code 72 (KEY_KP8)
85# Event code 73 (KEY_KP9)
86# Event code 74 (KEY_KPMINUS)
87# Event code 75 (KEY_KP4)
88# Event code 76 (KEY_KP5)
89# Event code 77 (KEY_KP6)
90# Event code 78 (KEY_KPPLUS)
91# Event code 79 (KEY_KP1)
92# Event code 80 (KEY_KP2)
93# Event code 81 (KEY_KP3)
94# Event code 82 (KEY_KP0)
95# Event code 83 (KEY_KPDOT)
96# Event code 85 (KEY_ZENKAKUHANKAKU)
97# Event code 86 (KEY_102ND)
98# Event code 87 (KEY_F11)
99# Event code 88 (KEY_F12)
100# Event code 89 (KEY_RO)
101# Event code 90 (KEY_KATAKANA)
102# Event code 91 (KEY_HIRAGANA)
103# Event code 92 (KEY_HENKAN)
104# Event code 93 (KEY_KATAKANAHIRAGANA)
105# Event code 94 (KEY_MUHENKAN)
106# Event code 95 (KEY_KPJPCOMMA)
107# Event code 96 (KEY_KPENTER)
108# Event code 97 (KEY_RIGHTCTRL)
109# Event code 98 (KEY_KPSLASH)
110# Event code 99 (KEY_SYSRQ)
111# Event code 100 (KEY_RIGHTALT)
112# Event code 102 (KEY_HOME)
113# Event code 103 (KEY_UP)
114# Event code 104 (KEY_PAGEUP)
115# Event code 105 (KEY_LEFT)
116# Event code 106 (KEY_RIGHT)
117# Event code 107 (KEY_END)
118# Event code 108 (KEY_DOWN)
119# Event code 109 (KEY_PAGEDOWN)
120# Event code 110 (KEY_INSERT)
121# Event code 111 (KEY_DELETE)
122# Event code 112 (KEY_MACRO)
123# Event code 113 (KEY_MUTE)
124# Event code 114 (KEY_VOLUMEDOWN)
125# Event code 115 (KEY_VOLUMEUP)
126# Event code 116 (KEY_POWER)
127# Event code 117 (KEY_KPEQUAL)
128# Event code 118 (KEY_KPPLUSMINUS)
129# Event code 119 (KEY_PAUSE)
130# Event code 121 (KEY_KPCOMMA)
131# Event code 122 (KEY_HANGEUL)
132# Event code 123 (KEY_HANJA)
133# Event code 124 (KEY_YEN)
134# Event code 125 (KEY_LEFTMETA)
135# Event code 126 (KEY_RIGHTMETA)
136# Event code 127 (KEY_COMPOSE)
137# Event code 128 (KEY_STOP)
138# Event code 140 (KEY_CALC)
139# Event code 142 (KEY_SLEEP)
140# Event code 143 (KEY_WAKEUP)
141# Event code 155 (KEY_MAIL)
142# Event code 156 (KEY_BOOKMARKS)
143# Event code 157 (KEY_COMPUTER)
144# Event code 158 (KEY_BACK)
145# Event code 159 (KEY_FORWARD)
146# Event code 163 (KEY_NEXTSONG)
147# Event code 164 (KEY_PLAYPAUSE)
148# Event code 165 (KEY_PREVIOUSSONG)
149# Event code 166 (KEY_STOPCD)
150# Event code 172 (KEY_HOMEPAGE)
151# Event code 173 (KEY_REFRESH)
152# Event code 183 (KEY_F13)
153# Event code 184 (KEY_F14)
154# Event code 185 (KEY_F15)
155# Event code 217 (KEY_SEARCH)
156# Event code 226 (KEY_MEDIA)
157# Event type 4 (EV_MSC)
158# Event code 4 (MSC_SCAN)
159# Event type 17 (EV_LED)
160# Event code 0 (LED_NUML)
161# Event code 1 (LED_CAPSL)
162# Event code 2 (LED_SCROLLL)
163# Event type 20 (EV_REP)
164# Properties:
165# N: AT Translated Set 2 keyboard
166# I: 0011 0001 0001 ab83
167# P: 00 00 00 00 00 00 00 00
168# B: 00 13 00 12 00 00 00 00 00
169# B: 01 fe ff ff ff ff ff ff ff
170# B: 01 ff ff ef ff df ff ff fe
171# B: 01 01 d0 00 f8 78 30 80 03
172# B: 01 00 00 00 02 04 00 00 00
173# B: 01 00 00 00 00 00 00 00 00
174# B: 01 00 00 00 00 00 00 00 00
175# B: 01 00 00 00 00 00 00 00 00
176# B: 01 00 00 00 00 00 00 00 00
177# B: 01 00 00 00 00 00 00 00 00
178# B: 01 00 00 00 00 00 00 00 00
179# B: 01 00 00 00 00 00 00 00 00
180# B: 01 00 00 00 00 00 00 00 00
181# B: 02 00 00 00 00 00 00 00 00
182# B: 03 00 00 00 00 00 00 00 00
183# B: 04 10 00 00 00 00 00 00 00
184# B: 05 00 00 00 00 00 00 00 00
185# B: 11 07 00 00 00 00 00 00 00
186# B: 12 00 00 00 00 00 00 00 00
187# B: 15 00 00 00 00 00 00 00 00
188# B: 15 00 00 00 00 00 00 00 00
189################################
190# Waiting for events #
191################################
192E: 0.000000 0004 0004 0028 # EV_MSC / MSC_SCAN 28
193E: 0.000000 0001 001c 0000 # EV_KEY / KEY_ENTER 0
194E: 0.000000 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
195E: 0.825945 0004 0004 0054 # EV_MSC / MSC_SCAN 54
196E: 0.825945 0001 0036 0001 # EV_KEY / KEY_RIGHTSHIFT 1
197E: 0.825945 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
198E: 1.101404 0004 0004 0054 # EV_MSC / MSC_SCAN 54
199E: 1.101404 0001 0036 0002 # EV_KEY / KEY_RIGHTSHIFT 2
200E: 1.101404 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
201E: 1.131449 0004 0004 0054 # EV_MSC / MSC_SCAN 54
202E: 1.131449 0001 0036 0002 # EV_KEY / KEY_RIGHTSHIFT 2
203E: 1.131449 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
204E: 1.161472 0004 0004 0036 # EV_MSC / MSC_SCAN 36
205E: 1.161472 0001 0024 0001 # EV_KEY / KEY_J 1
206E: 1.161472 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
207E: 1.221981 0004 0004 0036 # EV_MSC / MSC_SCAN 36
208E: 1.221981 0001 0024 0000 # EV_KEY / KEY_J 0
209E: 1.221981 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
210E: 1.241906 0004 0004 0054 # EV_MSC / MSC_SCAN 54
211E: 1.241906 0001 0036 0000 # EV_KEY / KEY_RIGHTSHIFT 0
212E: 1.241906 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
213E: 1.346773 0004 0004 0032 # EV_MSC / MSC_SCAN 32
214E: 1.346773 0001 0020 0001 # EV_KEY / KEY_D 1
215E: 1.346773 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
216E: 1.432213 0004 0004 0032 # EV_MSC / MSC_SCAN 32
217E: 1.432213 0001 0020 0000 # EV_KEY / KEY_D 0
218E: 1.432213 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
219E: 1.562060 0004 0004 0025 # EV_MSC / MSC_SCAN 25
220E: 1.562060 0001 0019 0001 # EV_KEY / KEY_P 1
221E: 1.562060 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
222E: 1.622477 0004 0004 0025 # EV_MSC / MSC_SCAN 25
223E: 1.622477 0001 0019 0000 # EV_KEY / KEY_P 0
224E: 1.622477 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
225E: 1.687305 0004 0004 0025 # EV_MSC / MSC_SCAN 25
226E: 1.687305 0001 0019 0001 # EV_KEY / KEY_P 1
227E: 1.687305 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
228E: 1.777807 0004 0004 0025 # EV_MSC / MSC_SCAN 25
229E: 1.777807 0001 0019 0000 # EV_KEY / KEY_P 0
230E: 1.777807 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
231E: 1.867577 0004 0004 0031 # EV_MSC / MSC_SCAN 31
232E: 1.867577 0001 001f 0001 # EV_KEY / KEY_S 1
233E: 1.867577 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
234E: 1.953019 0004 0004 0031 # EV_MSC / MSC_SCAN 31
235E: 1.953019 0001 001f 0000 # EV_KEY / KEY_S 0
236E: 1.953019 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
237E: 2.118004 0004 0004 0054 # EV_MSC / MSC_SCAN 54
238E: 2.118004 0001 0036 0001 # EV_KEY / KEY_RIGHTSHIFT 1
239E: 2.118004 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
240E: 2.348316 0004 0004 0002 # EV_MSC / MSC_SCAN 2
241E: 2.348316 0001 0002 0001 # EV_KEY / KEY_1 1
242E: 2.348316 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
243E: 2.489561 0004 0004 0002 # EV_MSC / MSC_SCAN 2
244E: 2.489561 0001 0002 0000 # EV_KEY / KEY_1 0
245E: 2.489561 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
246E: 2.523740 0004 0004 0054 # EV_MSC / MSC_SCAN 54
247E: 2.523740 0001 0036 0000 # EV_KEY / KEY_RIGHTSHIFT 0
248E: 2.523740 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
249E: 6.714286 0004 0004 0029 # EV_MSC / MSC_SCAN 29
250E: 6.714286 0001 001d 0001 # EV_KEY / KEY_LEFTCTRL 1
251E: 6.714286 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
252E: 6.989728 0004 0004 0029 # EV_MSC / MSC_SCAN 29
253E: 6.989728 0001 001d 0002 # EV_KEY / KEY_LEFTCTRL 2
254E: 6.989728 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
255E: 7.019767 0004 0004 0029 # EV_MSC / MSC_SCAN 29
256E: 7.019767 0001 001d 0002 # EV_KEY / KEY_LEFTCTRL 2
257E: 7.019767 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
258E: 7.049847 0004 0004 0029 # EV_MSC / MSC_SCAN 29
259E: 7.049847 0001 001d 0002 # EV_KEY / KEY_LEFTCTRL 2
260E: 7.049847 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
261E: 7.079889 0004 0004 0029 # EV_MSC / MSC_SCAN 29
262E: 7.079889 0001 001d 0002 # EV_KEY / KEY_LEFTCTRL 2
263E: 7.079889 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
264E: 7.109934 0004 0004 0029 # EV_MSC / MSC_SCAN 29
265E: 7.109934 0001 001d 0002 # EV_KEY / KEY_LEFTCTRL 2
266E: 7.109934 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
267E: 7.139943 0004 0004 0029 # EV_MSC / MSC_SCAN 29
268E: 7.139943 0001 001d 0002 # EV_KEY / KEY_LEFTCTRL 2
269E: 7.139943 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
270E: 7.170005 0004 0004 0023 # EV_MSC / MSC_SCAN 23
271E: 7.170005 0001 0017 0001 # EV_KEY / KEY_I 1
272E: 7.170005 0000 0000 0000 # ------------ SYN_REPORT (0) ----------
0273
=== modified file 'tests/unit-tests/CMakeLists.txt'
--- tests/unit-tests/CMakeLists.txt 2015-09-07 11:54:56 +0000
+++ tests/unit-tests/CMakeLists.txt 2015-09-17 09:09:28 +0000
@@ -25,6 +25,7 @@
25 ${GBM_INCLUDE_DIRS}25 ${GBM_INCLUDE_DIRS}
26 ${UMOCKDEV_INCLUDE_DIRS}26 ${UMOCKDEV_INCLUDE_DIRS}
2727
28 ${PROJECT_SOURCE_DIR}/include/cookie
28 ${PROJECT_SOURCE_DIR}/src/include/platform29 ${PROJECT_SOURCE_DIR}/src/include/platform
29 ${PROJECT_SOURCE_DIR}/src/include/server30 ${PROJECT_SOURCE_DIR}/src/include/server
30 ${PROJECT_SOURCE_DIR}/src/include/client31 ${PROJECT_SOURCE_DIR}/src/include/client
@@ -67,6 +68,7 @@
67 test_shared_library_prober.cpp68 test_shared_library_prober.cpp
68 test_lockable_callback.cpp69 test_lockable_callback.cpp
69 test_module_deleter.cpp70 test_module_deleter.cpp
71 test_mir_cookie.cpp
70)72)
7173
72if (MIR_TEST_PLATFORM STREQUAL "mesa-x11")74if (MIR_TEST_PLATFORM STREQUAL "mesa-x11")
7375
=== added file 'tests/unit-tests/test_mir_cookie.cpp'
--- tests/unit-tests/test_mir_cookie.cpp 1970-01-01 00:00:00 +0000
+++ tests/unit-tests/test_mir_cookie.cpp 2015-09-17 09:09:28 +0000
@@ -0,0 +1,105 @@
1/*
2 * Copyright © 2015 Canonical Ltd.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 3 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * Authored by: Christopher James Halse Rogers <christopher.halse.rogers@canonical.com>
17 */
18
19#include "mir_toolkit/mir_client_library.h"
20#include "mir/cookie_factory.h"
21
22#include "mir_test_framework/headless_test.h"
23#include "mir_test_framework/connected_client_with_a_surface.h"
24#include "mir/test/doubles/wrap_shell_to_track_latest_surface.h"
25#include "mir/shell/shell_wrapper.h"
26#include "mir/test/validity_matchers.h"
27#include "mir/test/wait_condition.h"
28
29#include "boost/throw_exception.hpp"
30
31#include <gtest/gtest.h>
32
33namespace mtf = mir_test_framework;
34namespace mtd = mir::test::doubles;
35namespace msh = mir::shell;
36
37TEST(MirCookieFactory, attests_real_timestamp)
38{
39 std::vector<uint8_t> secret{ 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0xde, 0x01 };
40 auto factory = mir::cookie::CookieFactory::create_from_secret(secret);
41
42 uint64_t mock_timestamp{0x322322322332};
43
44 auto cookie = factory->timestamp_to_cookie(mock_timestamp);
45
46 EXPECT_TRUE(factory->attest_timestamp(cookie));
47}
48
49TEST(MirCookieFactory, doesnt_attest_faked_timestamp)
50{
51 std::vector<uint8_t> secret{ 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0xde, 0x01 };
52 auto factory = mir::cookie::CookieFactory::create_from_secret(secret);
53
54 MirCookie bad_client_no_biscuit{ 0x33221100, 0x33221100 };
55
56 EXPECT_FALSE(factory->attest_timestamp(bad_client_no_biscuit));
57}
58
59TEST(MirCookieFactory, timestamp_trusted_with_different_secret_doesnt_attest)
60{
61 std::vector<uint8_t> alice{ 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff, 0xde, 0x01 };
62 std::vector<uint8_t> bob{ 0x01, 0x02, 0x44, 0xd8, 0xee, 0x0f, 0xde, 0x01 };
63
64 auto alices_factory = mir::cookie::CookieFactory::create_from_secret(alice);
65 auto bobs_factory = mir::cookie::CookieFactory::create_from_secret(bob);
66
67 uint64_t mock_timestamp{0x01020304};
68
69 auto alices_cookie = alices_factory->timestamp_to_cookie(mock_timestamp);
70 auto bobs_cookie = bobs_factory->timestamp_to_cookie(mock_timestamp);
71
72 EXPECT_FALSE(alices_factory->attest_timestamp(bobs_cookie));
73 EXPECT_FALSE(bobs_factory->attest_timestamp(alices_cookie));
74}
75
76TEST(MirCookieFactory, throw_when_secret_size_to_small)
77{
78 std::vector<uint8_t> bob(mir::cookie::CookieFactory::minimum_secret_size - 1);
79 EXPECT_THROW({
80 auto factory = mir::cookie::CookieFactory::create_from_secret(bob);
81 }, std::logic_error);
82}
83
84TEST(MirCookieFactory, saves_a_secret)
85{
86 std::vector<uint8_t> secret;
87 unsigned secret_size = 64;
88
89 mir::cookie::CookieFactory::create_saving_secret(secret, secret_size);
90
91 EXPECT_EQ(secret.size(), secret_size);
92}
93
94TEST(MirCookieFactory, timestamp_trusted_with_saved_secret_does_attest)
95{
96 uint64_t timestamp = 23;
97 unsigned secret_size = 64;
98 std::vector<uint8_t> secret;
99
100 auto source_factory = mir::cookie::CookieFactory::create_saving_secret(secret, secret_size);
101 auto sink_factory = mir::cookie::CookieFactory::create_from_secret(secret);
102 auto cookie = source_factory->timestamp_to_cookie(timestamp);
103
104 EXPECT_TRUE(sink_factory->attest_timestamp(cookie));
105}
0106
=== modified file 'tools/update_package_abis.sh'
--- tools/update_package_abis.sh 2015-08-07 05:46:34 +0000
+++ tools/update_package_abis.sh 2015-09-17 09:09:28 +0000
@@ -16,6 +16,7 @@
16 libmirplatform:MIRPLATFORM_ABI \16 libmirplatform:MIRPLATFORM_ABI \
17 libmirprotobuf:MIRPROTOBUF_ABI \17 libmirprotobuf:MIRPROTOBUF_ABI \
18 libmirserver:MIRSERVER_ABI \18 libmirserver:MIRSERVER_ABI \
19 libmircookie:MIRCOOKIE_ABI \
19 mir-client-platform-android:MIR_CLIENT_PLATFORM_ABI \20 mir-client-platform-android:MIR_CLIENT_PLATFORM_ABI \
20 mir-client-platform-mesa:MIR_CLIENT_PLATFORM_ABI \21 mir-client-platform-mesa:MIR_CLIENT_PLATFORM_ABI \
21 mir-platform-graphics-android:MIR_SERVER_GRAPHICS_PLATFORM_ABI \22 mir-platform-graphics-android:MIR_SERVER_GRAPHICS_PLATFORM_ABI \

Subscribers

People subscribed via source and target branches