This commit is contained in:
Jeffrey Griffin 2019-12-19 11:05:19 -08:00
commit e8ca5ddb7f
325 changed files with 89029 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*~

661
LICENSE Normal file
View File

@ -0,0 +1,661 @@
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<http://www.gnu.org/licenses/>.

147
README.md Normal file
View File

@ -0,0 +1,147 @@
# Secure Value Recovery Service (Beta)
## Building the SGX enclave (optional)
### Building reproducibly with Docker
#### Prerequisites:
- GNU Make
- Docker (able to run debian image)
`````
$ make -C <repository_root>/enclave
`````
The default docker-install target will create a reproducible build environment image using
`enclave/docker/Dockerfile`, build the enclave inside a container based on the image, and
install the resulting enclave into `service/kbupd/res/enclave/`. The Dockerfile will
download a stock dated-snapshot debian Docker image. The Debian project builds their
docker images reproducibly, based on the a snapshot of the debian repos on the date of the
build from the [Debian Snapshot Project](https://snapshot.debian.org/). Make will then be
run inside the newly built Docker Debian image as in the [Building with
Debian](#building-with-debian) section below:
NB: the installed enclave will be signed with the SGX debug flag enabled by an
automatically generated signing key. Due to Intel SGX licensing requirements, a debug
enclave can currently only be run with SGX debugging enabled, allowing inspection of its
encrypted memory, and invalidating its security properties. To use an enclave in
production, provide the Intel-whitelisted signing key as
`enclave/libkbupd_enclave.hardened.key` before building. Alternatively, the generated
`enclave/build/libkbupd_enclave.hardened.signdata` file can be signed and saved as
`enclave/build/libkbupd_enclave.sig` with corresponding public key at
`enclave/libkbupd_enclave.pub`, and signed using `make sign install`.
### Building with Debian
#### Prerequisites:
- GNU Make
- cmake
- ninja-build
- gcc
- ocaml-native-compilers
- ocamlbuild
- automake/autoconf/libtool/pkg-config
- libssl-dev
- libcurl4-openssl-dev
- protobuf-compiler
- libprotobuf-dev
- llvm-dev
- libclang-dev
- clang
- git
- devscripts/debhelper/fakeroot
- rust 1.37.0 toolchain from rustup
- [Intel SGX SDK v2.7.1 SDK](https://github.com/intel/linux-sgx/tree/sgx_2.7.1) build dependencies
`````
$ make -C <repository_root>/enclave debuild install
`````
`debuild` is a debian tool used to build debian packages after it sanitizes the
environment and installs build dependences. The primary advantage of using debian
packaging tools in this case is to leverage the [Reproducible
Builds](https://wiki.debian.org/ReproducibleBuilds) project. While building a debian
package, `debuild` will record the names and versions of all detected build dependencies
into a *.buildinfo file, for future reproducibility debugging.
The `debuild` target also builds parts needed from the Intel SGX SDK v2.7.1 after cloning it
from github.
The `install` target copies the enclave to `service/kbupd/res/enclave/`, which should
potentially be checked in to be used with the service.
The `sign` target may also be used as described in [Building reproducibly with
Docker](#building-reproducibly-with-docker) to produce a release-mode enclave.
### Building without Docker or Debian:
#### Prerequisites:
- GNU Make
- cmake
- ninja-build
- gcc
- ocaml-native-compilers
- ocamlbuild
- automake/autoconf/libtool/pkg-config
- libssl-dev
- libcurl4-openssl-dev
- protobuf-compiler
- libprotobuf-dev
- llvm-dev
- libclang-dev
- clang
- git
- rust 1.37.0 toolchain from rustup
- [Intel SGX SDK v2.7.1 SDK](https://github.com/intel/linux-sgx/tree/sgx_2.7.1) build dependencies
`````
$ make -C <repository_root>/enclave all install
`````
The `all` target will probably fail to reproduce the same binary as above, but doesn't
require Docker or Debian Linux.
The `sign` target may also be used as described in [Building reproducibly with
Docker](#building-reproducibly-with-docker) to produce a release-mode enclave.
## Building the service
### Building with Docker
#### Prerequisites:
- GNU Make
- Docker (able to run ubuntu image)
`````
$ make -C <repository_root>/service docker
`````
### Building without Docker
#### Prerequisites:
- GNU Make
- a C compiler
- rust toolchain (i.e. rustc, cargo)
- libsgx-enclave-common [from source](https://github.com/intel/linux-sgx/tree/master#install-the-intelr-sgx-psw) or [prebuilt](https://download.01.org/intel-sgx/sgx_repo/ubuntu/pool/main/libs/libsgx-enclave-common/)
- libssl-dev (OpenSSL)
- libseccomp-dev
- pkg-config
- protobuf-compiler
- [Intel SGX SDK SDK](https://github.com/intel/linux-sgx) headers (common/inc/sgx*.h) installed in a system include directory
`````
$ make -C <repository_root>/service all
`````
## Running the service
### Runtime requirements:
- libsgx-enclave-common >= 2.7.101 [from source](https://github.com/intel/linux-sgx/tree/master#install-the-intelr-sgx-psw) or [prebuilt](https://download.01.org/intel-sgx/sgx_repo/ubuntu/pool/main/libs/libsgx-enclave-common/)
- linux-sgx-driver >= 2.6.0 [from source](https://github.com/intel/linux-sgx-driver) or [prebuilt](https://download.01.org/intel-sgx/sgx-linux/2.7.1/distro/ubuntu18.04-server/)
- libssl1.1 (OpenSSL)
- libseccomp2
- libprotobuf10
`````
$ service/build/target/release/kbupd help
`````

10
enclave/.gitignore vendored Normal file
View File

@ -0,0 +1,10 @@
/*.key
/*.pub
/build
/target
/debian/.debhelper
/debian/debhelper-build-stamp
/debian/kbupd-enclave/
/debian/kbupd-enclave.substvars
/debian/*.deb
/debian/files

819
enclave/Cargo.lock generated Normal file
View File

@ -0,0 +1,819 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "arrayref"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "autocfg"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "backtrace"
version = "0.3.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace-sys"
version = "0.1.31"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "base64"
version = "0.10.1"
source = "git+https://github.com/marshallpierce/rust-base64.git?rev=07b1d6b713cc2bd7d107185bd0d14bf06cddfb48#07b1d6b713cc2bd7d107185bd0d14bf06cddfb48"
[[package]]
name = "bumpalo"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "bytes"
version = "0.5.0"
source = "git+https://github.com/tokio-rs/bytes.git?rev=ebe96021b0eaf52be1fedd0a925f4384275c9cc4#ebe96021b0eaf52be1fedd0a925f4384275c9cc4"
[[package]]
name = "c2-chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cc"
version = "1.0.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "chrono"
version = "0.4.9"
source = "git+https://github.com/geogriff-signal/chrono.git?rev=de22e82a1b00b8f015a1d736e497c3177e8e8c9f#de22e82a1b00b8f015a1d736e497c3177e8e8c9f"
dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "either"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "failure"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "failure_derive"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
"synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hashbrown"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "heck"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "indoc"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"indoc-impl 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "indoc-impl"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"unindent 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "intrusive-collections"
version = "0.8.1"
source = "git+https://github.com/geogriff-signal/intrusive-rs.git?rev=3c14ea97598616a2beaa97cd5fbbb27ba574fe77#3c14ea97598616a2beaa97cd5fbbb27ba574fe77"
dependencies = [
"memoffset 0.5.1 (git+https://github.com/Gilnaa/memoffset.git?rev=c14a1633760b55999a687bbdf17cb1fa3d1b2f9a)",
]
[[package]]
name = "itertools"
version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "itertools"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "itoa"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "js-sys"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"wasm-bindgen 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "kbupd_enclave"
version = "0.1.0"
dependencies = [
"base64 0.10.1 (git+https://github.com/marshallpierce/rust-base64.git?rev=07b1d6b713cc2bd7d107185bd0d14bf06cddfb48)",
"bytes 0.5.0 (git+https://github.com/tokio-rs/bytes.git?rev=ebe96021b0eaf52be1fedd0a925f4384275c9cc4)",
"chrono 0.4.9 (git+https://github.com/geogriff-signal/chrono.git?rev=de22e82a1b00b8f015a1d736e497c3177e8e8c9f)",
"hashbrown 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"intrusive-collections 0.8.1 (git+https://github.com/geogriff-signal/intrusive-rs.git?rev=3c14ea97598616a2beaa97cd5fbbb27ba574fe77)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"mockers 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"mockers_derive 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"no-std-compat 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"prost 0.5.0 (git+https://github.com/geogriff-signal/prost.git?rev=907f7d6e714d0b449d75269d532561c71d3ed250)",
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.40 (git+https://github.com/geogriff-signal/serde_json.git?rev=d79b0c67f62e168d4872bb8694377ffd97b8949f)",
"sgx_ffi 0.1.0",
"sgxsd_ffi 0.1.0",
"snow 0.6.1 (git+https://github.com/geogriff-signal/snow.git?rev=d8d00a37c8e39b2557d23a26cc4f722595b4f2d9)",
"spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"test_ffi 0.1.0",
"webpki 0.21.0 (git+https://github.com/geogriff-signal/webpki.git?rev=32ab63c46edfbfe9c1fe68008ba8e247eb387ca3)",
]
[[package]]
name = "lazy_static"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "libc"
version = "0.2.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "log"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "memchr"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "memoffset"
version = "0.5.1"
source = "git+https://github.com/Gilnaa/memoffset.git?rev=c14a1633760b55999a687bbdf17cb1fa3d1b2f9a#c14a1633760b55999a687bbdf17cb1fa3d1b2f9a"
dependencies = [
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mockers"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"select-rustc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "mockers_derive"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"indoc 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-quote 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "no-std-compat"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "nom"
version = "4.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-integer"
version = "0.1.41"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "num-traits"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ppv-lite86"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro-hack"
version = "0.5.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "proc-macro2"
version = "0.4.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "proc-macro2"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "proc-quote"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-quote-impl 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "proc-quote-impl"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "prost"
version = "0.5.0"
source = "git+https://github.com/geogriff-signal/prost.git?rev=907f7d6e714d0b449d75269d532561c71d3ed250#907f7d6e714d0b449d75269d532561c71d3ed250"
dependencies = [
"bytes 0.5.0 (git+https://github.com/tokio-rs/bytes.git?rev=ebe96021b0eaf52be1fedd0a925f4384275c9cc4)",
"prost-derive 0.5.0 (git+https://github.com/geogriff-signal/prost.git?rev=907f7d6e714d0b449d75269d532561c71d3ed250)",
]
[[package]]
name = "prost-derive"
version = "0.5.0"
source = "git+https://github.com/geogriff-signal/prost.git?rev=907f7d6e714d0b449d75269d532561c71d3ed250#907f7d6e714d0b449d75269d532561c71d3ed250"
dependencies = [
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_chacha"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ring"
version = "0.16.9"
source = "git+https://github.com/geogriff-signal/ring.git?rev=d0b3a4dcf1f24a2700280f9f7541cb5079398ec6#d0b3a4dcf1f24a2700280f9f7541cb5079398ec6"
dependencies = [
"cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"web-sys 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-demangle"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "rustc_version"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "ryu"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "select-rustc"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "semver-parser"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "serde"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_derive"
version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "serde_json"
version = "1.0.40"
source = "git+https://github.com/geogriff-signal/serde_json.git?rev=d79b0c67f62e168d4872bb8694377ffd97b8949f#d79b0c67f62e168d4872bb8694377ffd97b8949f"
dependencies = [
"itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)",
"ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sgx_ffi"
version = "0.1.0"
dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"mockers 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"mockers_derive 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"test_ffi 0.1.0",
]
[[package]]
name = "sgxsd_ffi"
version = "0.1.0"
dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"mockers 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"mockers_derive 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"sgx_ffi 0.1.0",
"test_ffi 0.1.0",
]
[[package]]
name = "snow"
version = "0.6.1"
source = "git+https://github.com/geogriff-signal/snow.git?rev=d8d00a37c8e39b2557d23a26cc4f722595b4f2d9#d8d00a37c8e39b2557d23a26cc4f722595b4f2d9"
dependencies = [
"arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"subtle 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sourcefile"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "subtle"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "0.15.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "synstructure"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "test_ffi"
version = "0.1.0"
dependencies = [
"libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
"mockers 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"mockers_derive 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-segmentation"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unindent"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "untrusted"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "version_check"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "wasm-bindgen"
version = "0.2.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-macro 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-shared 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-macro-support 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-backend 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-shared 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "wasm-bindgen-webidl"
version = "0.2.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-backend 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
"weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "web-sys"
version = "0.3.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"js-sys 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)",
"sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-webidl 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "webpki"
version = "0.21.0"
source = "git+https://github.com/geogriff-signal/webpki.git?rev=32ab63c46edfbfe9c1fe68008ba8e247eb387ca3#32ab63c46edfbfe9c1fe68008ba8e247eb387ca3"
dependencies = [
"ring 0.16.9 (git+https://github.com/geogriff-signal/ring.git?rev=d0b3a4dcf1f24a2700280f9f7541cb5079398ec6)",
"untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "weedle"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum arrayref 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "0d382e583f07208808f6b1249e60848879ba3543f57c32277bf52d69c2f0f0ee"
"checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875"
"checksum backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)" = "690a62be8920ccf773ee00ef0968649b0e724cda8bd5b12286302b4ae955fdf5"
"checksum backtrace-sys 0.1.31 (registry+https://github.com/rust-lang/crates.io-index)" = "82a830b4ef2d1124a711c71d263c5abdc710ef8e907bd508c88be475cebc422b"
"checksum base64 0.10.1 (git+https://github.com/marshallpierce/rust-base64.git?rev=07b1d6b713cc2bd7d107185bd0d14bf06cddfb48)" = "<none>"
"checksum bumpalo 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad807f2fc2bf185eeb98ff3a901bd46dc5ad58163d0fa4577ba0d25674d71708"
"checksum bytes 0.5.0 (git+https://github.com/tokio-rs/bytes.git?rev=ebe96021b0eaf52be1fedd0a925f4384275c9cc4)" = "<none>"
"checksum c2-chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7d64d04786e0f528460fc884753cf8dddcc466be308f6026f8e355c41a0e4101"
"checksum cc 1.0.45 (registry+https://github.com/rust-lang/crates.io-index)" = "4fc9a35e1f4290eb9e5fc54ba6cf40671ed2a2514c3eeb2b2a908dda2ea5a1be"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum chrono 0.4.9 (git+https://github.com/geogriff-signal/chrono.git?rev=de22e82a1b00b8f015a1d736e497c3177e8e8c9f)" = "<none>"
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
"checksum hashbrown 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2bcea5b597dd98e6d1f1ec171744cc5dee1a30d1c23c5b98e3cf9d4fbdf8a526"
"checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205"
"checksum indoc 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3f9553c1e16c114b8b77ebeb329e5f2876eed62a8d51178c8bc6bff0d65f98f8"
"checksum indoc-impl 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b714fc08d0961716390977cdff1536234415ac37b509e34e5a983def8340fb75"
"checksum intrusive-collections 0.8.1 (git+https://github.com/geogriff-signal/intrusive-rs.git?rev=3c14ea97598616a2beaa97cd5fbbb27ba574fe77)" = "<none>"
"checksum itertools 0.4.19 (registry+https://github.com/rust-lang/crates.io-index)" = "c4a9b56eb56058f43dc66e58f40a214b2ccbc9f3df51861b63d51dec7b65bc3f"
"checksum itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b8467d9c1cebe26feb08c640139247fac215782d35371ade9a2136ed6085358"
"checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f"
"checksum js-sys 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)" = "1efc4f2a556c58e79c5500912e221dd826bec64ff4aabd8ce71ccef6da02d7d4"
"checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba"
"checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7"
"checksum memchr 2.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "88579771288728879b57485cc7d6b07d648c9f0141eb955f8ab7f9d45394468e"
"checksum memoffset 0.5.1 (git+https://github.com/Gilnaa/memoffset.git?rev=c14a1633760b55999a687bbdf17cb1fa3d1b2f9a)" = "<none>"
"checksum mockers 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8bf36d14570661eebd299640fff1fae157c6d7da34bcbf4e4eba1c3b47b46ec9"
"checksum mockers_derive 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a53c3de46bf8e9cbe2b80e5086bbc8659bdf508c3719d50c2f366c9368d5726c"
"checksum no-std-compat 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df270209a7f04d62459240d890ecb792714d5db12c92937823574a09930276b4"
"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
"checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09"
"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32"
"checksum ppv-lite86 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e3cbf9f658cdb5000fcf6f362b8ea2ba154b9f146a61c7a20d647034c6b6561b"
"checksum proc-macro-hack 0.5.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e688f31d92ffd7c1ddc57a1b4e6d773c0f2a14ee437a4b0a4f5a69c80eb221c8"
"checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
"checksum proc-macro2 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "afdc77cc74ec70ed262262942ebb7dac3d479e9e5cfa2da1841c0806f6cdabcc"
"checksum proc-quote 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fa612543f23fda013e1e6ce30b5285a9d313c6e582e57b4ceca74eb5b85685b5"
"checksum proc-quote-impl 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f785f0f8cd00b7945efc3f3bdf8205eb06af5aacec598d83e67f41dc8d101fda"
"checksum prost 0.5.0 (git+https://github.com/geogriff-signal/prost.git?rev=907f7d6e714d0b449d75269d532561c71d3ed250)" = "<none>"
"checksum prost-derive 0.5.0 (git+https://github.com/geogriff-signal/prost.git?rev=907f7d6e714d0b449d75269d532561c71d3ed250)" = "<none>"
"checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
"checksum quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "053a8c8bcc71fcce321828dc897a98ab9760bef03a4fc36693c231e5b3216cfe"
"checksum rand 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412"
"checksum rand_chacha 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "03a2a90da8c7523f554344f921aa97283eadf6ac484a6d2a7d0212fa7f8d6853"
"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
"checksum ring 0.16.9 (git+https://github.com/geogriff-signal/ring.git?rev=d0b3a4dcf1f24a2700280f9f7541cb5079398ec6)" = "<none>"
"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
"checksum rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
"checksum ryu 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997"
"checksum select-rustc 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fdb2e15b35a72de428af0da95ba4c990a336602576906f537923c5aa4d695835"
"checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
"checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
"checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd"
"checksum serde_derive 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "4b133a43a1ecd55d4086bd5b4dc6c1751c68b1bfbeba7a5040442022c7e7c02e"
"checksum serde_json 1.0.40 (git+https://github.com/geogriff-signal/serde_json.git?rev=d79b0c67f62e168d4872bb8694377ffd97b8949f)" = "<none>"
"checksum snow 0.6.1 (git+https://github.com/geogriff-signal/snow.git?rev=d8d00a37c8e39b2557d23a26cc4f722595b4f2d9)" = "<none>"
"checksum sourcefile 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "4bf77cb82ba8453b42b6ae1d692e4cdc92f9a47beaf89a847c8be83f4e328ad3"
"checksum spin 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
"checksum subtle 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "01f40907d9ffc762709e4ff3eb4a6f6b41b650375a3f09ac92b641942b7fb082"
"checksum syn 0.15.44 (registry+https://github.com/rust-lang/crates.io-index)" = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
"checksum syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "66850e97125af79138385e9b88339cbcd037e3f28ceab8c5ad98e64f0f1f80bf"
"checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f"
"checksum unicode-segmentation 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1967f4cdfc355b37fd76d2a954fb2ed3871034eb4f26d60537d88795cfc332a9"
"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
"checksum unindent 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c7d0d32a92c9ed197278e09140c32dec854ad5826f0e0e18c1d2a1690f15c8d5"
"checksum untrusted 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "60369ef7a31de49bcb3f6ca728d4ba7300d9a1658f94c727d4cab8c8d9f4aece"
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
"checksum wasm-bindgen 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "dcddca308b16cd93c2b67b126c688e5467e4ef2e28200dc7dfe4ae284f2faefc"
"checksum wasm-bindgen-backend 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "f805d9328b5fc7e5c6399960fd1889271b9b58ae17bdb2417472156cc9fafdd0"
"checksum wasm-bindgen-macro 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "3ff88201a482abfc63921621f6cb18eb1efd74f136b05e5841e7f8ca434539e9"
"checksum wasm-bindgen-macro-support 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "6a433d89ecdb9f77d46fcf00c8cf9f3467b7de9954d8710c175f61e2e245bb0e"
"checksum wasm-bindgen-shared 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "d41fc1bc3570cdf8d108c15e014045fd45a95bb5eb36605f96a90461fc34027d"
"checksum wasm-bindgen-webidl 0.2.50 (registry+https://github.com/rust-lang/crates.io-index)" = "be53d289bf2fa7645a089cfd5c7a34bf4fe94221f58cf86ee42a7b4bc854ff14"
"checksum web-sys 0.3.27 (registry+https://github.com/rust-lang/crates.io-index)" = "6435c477200ad486089a7a72c2bd6c9bdf9740bd7fff868806076218076d8c51"
"checksum webpki 0.21.0 (git+https://github.com/geogriff-signal/webpki.git?rev=32ab63c46edfbfe9c1fe68008ba8e247eb387ca3)" = "<none>"
"checksum weedle 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3bb43f70885151e629e2a19ce9e50bd730fd436cfd4b666894c9ce4de9141164"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

10
enclave/Cargo.toml Normal file
View File

@ -0,0 +1,10 @@
[workspace]
members = ["kbupd_enclave", "sgx_ffi", "sgxsd_ffi", "test_ffi"]
[patch.crates-io]
ring = { rev = "d0b3a4dcf1f24a2700280f9f7541cb5079398ec6", git = "https://github.com/geogriff-signal/ring.git" }
bytes = { rev = "ebe96021b0eaf52be1fedd0a925f4384275c9cc4", git = "https://github.com/tokio-rs/bytes.git" }
memoffset = { rev = "c14a1633760b55999a687bbdf17cb1fa3d1b2f9a", git = "https://github.com/Gilnaa/memoffset.git" }
[profile.release]
lto = "fat"

297
enclave/Makefile Normal file
View File

@ -0,0 +1,297 @@
sgxsd_srcdir = sgxsd_enclave
includedir = include
patchdir = patches
builddir = build
targetdir = $(builddir)/target
resourcedir = ../service/kbupd/res
RUSTC ?= rustc
CARGO ?= cargo
RUSTUP ?= rustup
BINDGEN ?= $(builddir)/bin/bindgen-0.51.1
DOCKER ?= docker
INSTALL ?= install
RUSTUP_TOOLCHAIN_UNSTABLE ?= nightly
FEATURES ?=
INSTALL_PROGRAM = $(INSTALL) -m 755 $(INSTALL_PROGRAM_FLAGS)
INSTALL_DATA = $(INSTALL) -m 644
DESTDIR ?=
CFLAGS = \
-m64 -O2 -ggdb -march=skylake -pipe -fPIC \
-D_FORTIFY_SOURCE=2 -std=c11 -D_DEFAULT_SOURCE \
-fstack-protector-strong -fcf-protection \
-Wall -Werror=all -Wextra -Wno-unused-parameter -Wno-missing-field-initializers \
-I$(includedir) -I$(includedir)/bearssl -I$(SGX_INCLUDEDIR)
LDFLAGS =
ENCLAVE_RUSTFLAGS = -C opt-level=2 -C debuginfo=1 -C codegen-units=1 -C panic=abort -Z allow_features=alloc_error_handler,thread_local -C llvm-args=-max-jump-table-size=1 -C llvm-args=-disable-tail-duplicate
BINDGENFLAGS =
CARGOBUILDFLAGS =
export CARGO_TARGET_DIR = $(CURDIR)/$(targetdir)
export CARGO_HOME = $(CURDIR)/$(builddir)/cargo
TEST_CFLAGS += $(CFLAGS) \
-DUNIT_TESTING -fsanitize=address -static-libasan -fsanitize=undefined -static-libubsan
##
## sgxsd
##
TEST_SGXSD_TARGET := $(builddir)/sgxsd-enclave-test
BEARSSL_SOURCES := \
$(sgxsd_srcdir)/bearssl/gcm.c $(sgxsd_srcdir)/bearssl/ghash_pclmul.c \
$(sgxsd_srcdir)/bearssl/sha2small.c $(sgxsd_srcdir)/bearssl/dec32be.c $(sgxsd_srcdir)/bearssl/enc32be.c \
$(sgxsd_srcdir)/bearssl/aes_x86ni_ctr.c $(sgxsd_srcdir)/bearssl/aes_x86ni.c
BEARSSL_OBJECTS := $(addprefix $(builddir)/,$(BEARSSL_SOURCES:.c=.o))
SGXSD_SOURCES := $(sgxsd_srcdir)/sgxsd-enclave.c $(sgxsd_srcdir)/curve25519-donna-c64.c $(BEARSSL_SOURCES) \
$(sgxsd_srcdir)/sgx-tcrypto-stub.c
SGXSD_OBJECTS := $(addprefix $(builddir)/,$(SGXSD_SOURCES:.c=.o))
TEST_SGXSD_SOURCES := $(sgxsd_srcdir)/sgxsd-enclave.c $(sgxsd_srcdir)/curve25519-donna-c64.c $(sgxsd_srcdir)/sgxsd-enclave-test.c $(sgxsd_srcdir)/cmockery.c
TEST_SGXSD_OBJECTS := $(addprefix $(builddir)/test/,$(TEST_SGXSD_SOURCES:.c=.o))
TEST_LDFLAGS += $(TEST_CFLAGS)
##
## kbupd
##
KBUPD_ENCLAVE_NAME := libkbupd_enclave.hardened
KBUPD_ENCLAVE_TARGET := $(builddir)/libkbupd_enclave.unstripped.so
KBUPD_ENCLAVE_RUST_STATICLIB := $(targetdir)/release/libkbupd_enclave.a
##
## targets
##
.PHONY: default docker-install all unstripped hardened unsigned llvm-bolt doc check test test-asan benchmark clippy bindgen protobuf debug sign install edger8r distclean clean docker
.SUFFIXES:
.SUFFIXES: .c .o
default: docker-install
include sgx_enclave.mk
docker-install: docker install
all: $(KBUPD_ENCLAVE_TARGET) $(builddir)/$(KBUPD_ENCLAVE_NAME).unstripped.so $(builddir)/$(KBUPD_ENCLAVE_NAME).unsigned.so $(builddir)/$(KBUPD_ENCLAVE_NAME).debug.so $(builddir)/$(KBUPD_ENCLAVE_NAME).signdata $(builddir)/$(KBUPD_ENCLAVE_NAME).mrenclave
unstripped: $(KBUPD_ENCLAVE_TARGET)
hardened: $(builddir)/$(KBUPD_ENCLAVE_NAME).unstripped.so
unsigned: $(builddir)/$(KBUPD_ENCLAVE_NAME).unsigned.so
llvm-bolt: $(LLVM_BOLT)
doc:
env -u CFLAGS RUSTC_BOOTSTRAP=1 RUSTFLAGS="$(ENCLAVE_RUSTFLAGS)" \
$(CARGO) doc --package=kbupd_enclave --release --document-private-items --lib
check:
$(CARGO) check --package=kbupd_enclave --lib --tests
test: $(TEST_SGXSD_TARGET)
ASAN_OPTIONS="detect_leaks=0:$(ASAN_OPTIONS)" ./$(TEST_SGXSD_TARGET)
env -u CFLAGS \
RUST_BACKTRACE=full \
RUST_TEST_THREADS=1 \
$(CARGO) test --all --exclude=kbupd_enclave -- --test-threads=1
env -u CFLAGS \
RUST_BACKTRACE=full \
RUST_TEST_THREADS=1 \
$(CARGO) test --manifest-path=kbupd_enclave/Cargo.toml --lib --bins --features test -- --test-threads=1
test-asan: $(TEST_SGXSD_TARGET)
./$(TEST_SGXSD_TARGET)
env -u CFLAGS \
RUST_BACKTRACE=full \
RUSTFLAGS="-Z sanitizer=address" RUST_TEST_THREADS=1 \
ASAN_OPTIONS="detect_odr_violation=1:detect_stack_use_after_return=true:check_initialization_order=true:strict_init_order=true:halt_on_error=false:$(ASAN_OPTIONS)" \
LSAN_OPTIONS="suppressions=$(CURDIR)/kbupd_enclave/lsan-ignore-test.txt:$(LSAN_OPTIONS)" \
$(RUSTUP) run $(RUSTUP_TOOLCHAIN_UNSTABLE) \
$(CARGO) test --all --exclude=kbupd_enclave -- --test-threads=1
env -u CFLAGS \
RUST_BACKTRACE=full \
RUSTFLAGS="-Z sanitizer=address" RUST_TEST_THREADS=1 \
ASAN_OPTIONS="detect_odr_violation=1:detect_stack_use_after_return=true:check_initialization_order=true:strict_init_order=true:halt_on_error=false:$(ASAN_OPTIONS)" \
LSAN_OPTIONS="suppressions=$(CURDIR)/kbupd_enclave/lsan-ignore-test.txt:$(LSAN_OPTIONS)" \
$(RUSTUP) run $(RUSTUP_TOOLCHAIN_UNSTABLE) \
$(CARGO) test --manifest-path=kbupd_enclave/Cargo.toml --lib --bins --tests --features=test -- --test-threads=1
clippy:
$(CARGO) clippy --all --exclude=kbupd_enclave
$(CARGO) clippy --manifest-path=kbupd_enclave/Cargo.toml --features=test
benchmark:
$(CARGO) bench --all
bindgen: $(BINDGEN) | $(SGX_INCLUDEDIR)
$(BINDGEN) --no-include-path-detection -o sgx_ffi/src/bindgen_wrapper.rs \
--rust-target 1.33 --use-core --ctypes-prefix libc --with-derive-default --with-derive-eq --no-prepend-enum-name \
sgx_ffi/src/bindgen_wrapper.h -- \
$(filter-out -fvisibility=hidden,$(filter-out -std=%,$(CFLAGS) $(ENCLAVE_CFLAGS)))
$(BINDGEN) --no-include-path-detection -o sgxsd_ffi/src/bindgen_wrapper.rs \
--rust-target 1.33 --use-core --ctypes-prefix libc --with-derive-default --with-derive-eq --no-prepend-enum-name \
sgxsd_ffi/src/bindgen_wrapper.h -- \
$(filter-out -fvisibility=hidden,$(filter-out -std=%,$(CFLAGS) $(ENCLAVE_CFLAGS)))
$(BINDGEN) --no-include-path-detection -o kbupd_enclave/src/ffi/bindgen_wrapper.rs \
--rust-target 1.33 --use-core --ctypes-prefix libc --with-derive-default --with-derive-eq --no-prepend-enum-name \
kbupd_enclave/src/ffi/bindgen_wrapper.h -- \
$(filter-out -fvisibility=hidden,$(filter-out -std=%,$(CFLAGS) $(ENCLAVE_CFLAGS)))
$(builddir)/bin/bindgen-%:
$(CARGO) install --force --version $* --bin bindgen bindgen
mkdir -p $(builddir)/bin
cp $(builddir)/cargo/bin/bindgen $@
protobuf: | $(targetdir)/debug/prostc
mkdir -p kbupd_enclave/src/protobufs
OUT_DIR=kbupd_enclave/src/protobufs $(targetdir)/debug/prostc kbupd_enclave/src/protobufs.proto kbupd_enclave/src/ $(includedir)/
mv kbupd_enclave/src/protobufs/protobufs.rs kbupd_enclave/src/protobufs/mod.rs
cd kbupd_enclave/src/protobufs; for file in protobufs.*.rs; do newfile=$${file#protobufs.}; mv $$file $$newfile; echo "pub mod $${newfile%.rs};" >> mod.rs; done
debug: $(builddir)/$(KBUPD_ENCLAVE_NAME).unsigned.so $(builddir)/$(KBUPD_ENCLAVE_NAME).debug.so
sign: $(builddir)/$(KBUPD_ENCLAVE_NAME).signed.so $(builddir)/$(KBUPD_ENCLAVE_NAME).test.signed.so
install:
$(INSTALL_DATA) $(builddir)/$(KBUPD_ENCLAVE_NAME).debug.so \
$(resourcedir)/enclave/$$(cat $(builddir)/$(KBUPD_ENCLAVE_NAME).mrenclave).so
if [ -e $(builddir)/$(KBUPD_ENCLAVE_NAME).signed.so ]; then \
cp $(builddir)/$(KBUPD_ENCLAVE_NAME).signed.so \
$(resourcedir)/enclave/$$(cat $(builddir)/$(KBUPD_ENCLAVE_NAME).mrenclave).so; \
fi
edger8r: $(includedir)/kbupd_enclave.edl | $(SGX_EDGER8R)
$(SGX_EDGER8R) --untrusted --untrusted-dir $(includedir) \
--trusted --trusted-dir $(includedir) \
--search-path $(SGX_INCLUDEDIR) \
--search-path $(includedir) \
$(includedir)/kbupd_enclave.edl
distclean: clean
clean:
rm -f $(builddir)/$(KBUPD_ENCLAVE_NAME).debug.signdata \
$(builddir)/$(KBUPD_ENCLAVE_NAME).debug.key \
$(builddir)/$(KBUPD_ENCLAVE_NAME).debug.pub \
$(builddir)/$(KBUPD_ENCLAVE_NAME).debug.sig \
$(builddir)/$(KBUPD_ENCLAVE_NAME).test.sig \
$(builddir)/$(KBUPD_ENCLAVE_NAME).test.signdata \
$(builddir)/$(KBUPD_ENCLAVE_NAME).sig \
$(builddir)/$(KBUPD_ENCLAVE_NAME).signdata \
$(builddir)/$(KBUPD_ENCLAVE_NAME).mrenclave \
$(builddir)/*.o \
$(builddir)/*.a \
$(builddir)/*.so \
$(SGXSD_OBJECTS) $(TEST_SGXSD_TARGET) $(TEST_SGXSD_OBJECTS) \
debian/debhelper-build-stamp \
debian/kbupd-enclave.substvars \
debian/files \
debian/*.deb
-rm -r $(targetdir)/release/ \
$(targetdir)/debug/ \
$(builddir)/bolt/build \
$(builddir)/cargo/bin \
$(SGX_SDK_SOURCE_DIR) \
debian/.debhelper/ \
debian/kbupd-enclave/
-$(CARGO) clean --release
## rust
.PHONY: FORCE
FORCE:
$(targetdir)/debug/prostc: FORCE
env -u CFLAGS $(CARGO) build --manifest-path=prostc/Cargo.toml --bin=prostc
$(targetdir)/release/lib%.a: FORCE
env CFLAGS="" RUSTC_BOOTSTRAP=1 RUSTFLAGS="$(ENCLAVE_RUSTFLAGS)" \
$(CARGO) build -vv --release --package=$* --lib $(if $(FEATURES),--features $(FEATURES))
## sgxsd
$(BEARSSL_OBJECTS): $(wildcard $(includedir)/bearssl/%.h)
$(SGXSD_OBJECTS): $(builddir)/%.o: %.c $(includedir)/sgxsd.h $(includedir)/sgxsd-enclave.h | $(SGX_INCLUDEDIR)
@mkdir -p $(dir $@)
$(CC) -o $@ $(CFLAGS) $(ENCLAVE_CFLAGS) -c $<
$(TEST_SGXSD_TARGET): $(TEST_SGXSD_OBJECTS)
$(CC) -o $@ $(TEST_SGXSD_OBJECTS) $(TEST_LDFLAGS)
$(TEST_SGXSD_OBJECTS): $(builddir)/test/%.o: %.c $(includedir)/sgxsd.h $(includedir)/sgxsd-enclave.h $(includedir)/cmockery.h | $(SGX_INCLUDEDIR)
@mkdir -p $(dir $@)
$(CC) -o $@ $(CFLAGS) $(TEST_CFLAGS) -c $<
## kbupd
$(includedir)/kbupd_enclave_t.h $(includedir)/kbupd_enclave_u.h: $(includedir)/sgxsd.edl
$(builddir)/kbupd_enclave_t.o: $(includedir)/kbupd_enclave_t.c
$(CC) -o $@ $(CFLAGS) $(ENCLAVE_CFLAGS) -c $<
$(builddir)/kbupd_enclave_u.o: $(includedir)/kbupd_enclave_u.c
$(CC) -o $@ $(CFLAGS) -c $<
$(builddir)/libkbupd_enclave_u.a: $(builddir)/kbupd_enclave_u.o
$(AR) r $@ $<
$(KBUPD_ENCLAVE_TARGET): $(SGXSD_OBJECTS) $(KBUPD_ENCLAVE_RUST_STATICLIB)
$(KBUPD_ENCLAVE_TARGET): LDFLAGS := -L$(dir $(KBUPD_ENCLAVE_RUST_STATICLIB))
$(KBUPD_ENCLAVE_TARGET): LDLIBS := -lkbupd_enclave
## Reproducible enclave build via debian package
MAKETARGET ?= bindgen debuild sign
docker: DOCKER_EXTRA=$(shell [ -L build ] && P=$$(readlink build) && echo -v $$P/:$$P )
docker:
$(DOCKER) build --build-arg UID=$$(id -u) --build-arg GID=$$(id -g) \
-t kbupd-enclave-builder ./docker
$(DOCKER) run -it --rm --user $$(id -u):$$(id -g) --cap-add SYS_PTRACE \
-v `pwd`/:/home/signal/src $(DOCKER_EXTRA) kbupd-enclave-builder \
sh -c "cd src; make $(MAKETARGET)"
.PHONY: debuild
debuild:
env -u LANG LC_ALL=C debuild --preserve-envvar=PATH --no-lintian --build=binary -uc -us -j1
mv ../*.buildinfo debian/buildinfo
mv ../*.deb debian/
.PHONY: debuild-kbupd-enclave-build
debuild-kbupd-enclave-build: all
.PHONY: debuild-kbupd-enclave-install
debuild-kbupd-enclave-install: $(builddir)/$(KBUPD_ENCLAVE_NAME).unsigned.so
mkdir -p $(DESTDIR)/usr/lib/kbupd/enclave/
$(INSTALL_DATA) $(builddir)/$(KBUPD_ENCLAVE_NAME).unsigned.so $(DESTDIR)/usr/lib/kbupd/enclave/
.PHONY: debuild-kbupd-enclave-test
debuild-kbupd-enclave-test:
echo "not running tests in debuild" 1>&2
.PHONY: tar
tar:
tar -cjf $(builddir)/$(KBUPD_ENCLAVE_NAME).build.tar.bz2 \
$(LLVM_BOLT) \
--anchored --exclude-vcs \
--exclude='$(builddir)/bolt' \
--exclude='$(builddir)/cargo/registry/cache' \
--exclude='$(builddir)/cargo/registry/index' \
--exclude='$(builddir)/cargo/git/db' \
--exclude='*.git' \
--no-wildcards-match-slash \
--exclude='$(builddir)/*.tar.bz2' \
--verbose --totals \
'$(builddir)/'

189
enclave/bin/gc_functions Executable file
View File

@ -0,0 +1,189 @@
#!/usr/bin/env python3
import sys, re, subprocess
from collections import namedtuple
Function = namedtuple('Function', 'length length_instructions offset refs')
start_func = re.compile(r'^([0-9a-f]+) <(.+)>:$')
instruction = re.compile(r'^\s*[0-9a-f]+:\s+((?:[0-9a-f]{2} )+)\s*([0-9a-z()]+)?(?:\w+(.+))?\w*$')
instruction_ref = re.compile(r'^[^<]*(?:<([^>+]+)(?:[+]([^>]+))?>)?$')
entry_point = re.compile(r'^\s*Entry point address:\s+0x([0-9a-f]+)$')
data_line = re.compile(r'^\s*0x[0-9a-f]+ ([0-9a-f]+) ([0-9a-f]+) ([0-9a-f]+) ([0-9a-f]+) .+$')
global_text = re.compile(r'^[0-9a-f]+ T (.+)$')
def parse(disasm):
funcs = {}
cur_func = None
cur_offset = 0
cur_len = 0
cur_len_instructions = 0
cur_refs = None
for line in disasm:
line = line.rstrip('\n')
if len(line) == 0:
continue
if line.startswith('Disassembly of section '):
continue
m = start_func.match(line)
if m:
if cur_func:
funcs[cur_func] = Function(length = cur_len,
length_instructions = cur_len_instructions,
offset = cur_offset,
refs = cur_refs)
cur_func = m.group(2)
cur_offset = m.group(1)
cur_len = 0
cur_len_instructions = 0
cur_refs = set()
continue
if not cur_func:
continue
m = instruction.match(line)
if m:
instruction_bytes = m.group(1)
instruction_opcode = m.group(2)
instruction_operands = m.group(3)
if not instruction_opcode:
#Padding. As a hack decrement isns count, since it isn't really one.
cur_len_instructions -= 1
if instruction_operands:
ref_match = instruction_ref.match(instruction_operands)
if ref_match:
ref = ref_match.group(1)
if ref:
cur_refs.add(ref.split('+')[0])
cur_len_instructions += 1
cur_len += instruction_bytes.count(' ')
else:
print("Couldn't parse '%s' " % line)
assert(False)
if cur_func:
funcs[cur_func] = Function(length = cur_len,
length_instructions = cur_len_instructions,
offset = cur_offset,
refs = cur_refs)
for key in tuple(funcs.keys()):
if key.startswith('.'):
del(funcs[key])
return funcs
def get_roots(binary, funcs):
roots = set()
candidates = []
inv_funcs = {v.offset: k for k, v in funcs.items()}
#Get the entry point, and scan various data sections for possible function pointers.
sections = [ '.data.rel.ro', '.rodata', '.data' ]
section_args = [ v for pair in zip(['-x'] * 3, sections) for v in pair ]
readelf = subprocess.run(['readelf', '-W', '-h', *section_args, binary],
stdout=subprocess.PIPE, universal_newlines=True)
for l in readelf.stdout.split('\n'):
m = entry_point.match(l)
if m:
roots.add(inv_funcs[m.group(1).rjust(16, '0')])
m = data_line.match(l)
if m:
#XXX This misses the trailing line if it has less than 16 bytes.
for i in 1, 3:
hi = m.group(i+1)
lo = m.group(i)
candidates.append("%s%s%s%s%s%s%s%s" % (hi[6:8], hi[4:6], hi[2:4], hi[0:2],
lo[6:8], lo[4:6], lo[2:4], lo[0:2]))
for c in candidates:
f = inv_funcs.get(c)
if f:
roots.add(f)
#Get exported .text segment symbols.
nm = subprocess.run(['nm', '-g', binary], stdout=subprocess.PIPE, universal_newlines=True)
for l in nm.stdout.split('\n'):
m = global_text.match(l)
if m:
roots.add(m.group(1))
return roots
def mark(root, funcs, markset):
# if root == 'GFp_bn_sqr8x_internal':
# import inspect
# raise Exception([f.frame.f_locals['root'] for f in inspect.stack() if f.function == 'mark'])
markset.add(root)
for ref in funcs[root].refs:
if ref == root:
continue
if ref in markset:
continue
if ref in funcs:
mark(ref, funcs, markset)
def clobber(binary, clobbers, funcs):
with open(binary, 'r+b') as binfile:
for clobber in clobbers:
func = funcs[clobber]
offset = int(func.offset, 16)
clobber_bytes = b'\xcc' * (func.length - 1)
clobber_bytes += b'\xc3'
binfile.seek(offset)
binfile.write(clobber_bytes)
def main():
assert(sys.argv[1].endswith('.so'))
disasm = subprocess.Popen(['objdump', '-d', sys.argv[1]],
stdout=subprocess.PIPE,
universal_newlines=True)
funcs = parse(disasm.stdout)
roots = get_roots(sys.argv[1], funcs)
print("Found %s roots" % len(roots))
marks = set()
for root in roots:
mark(root, funcs, marks)
deads = set(funcs) - marks
to_clobber = set()
byte_cnt = 0
for dead in deads:
#if dead.startswith("_Z"):
# continue #Skip Rust code for now.
byte_cnt += funcs[dead].length
print("Dead: %s - %s isns %s:%s" % (dead, funcs[dead].length_instructions,
funcs[dead].offset, funcs[dead].length))
to_clobber.add(dead)
print('Dead/Total functions: %s/%s (%.2f%%)' % (len(to_clobber), len(funcs),
len(to_clobber)/len(funcs)*100.0))
byte_total = 0
for f in funcs.values():
byte_total += f.length
print('Dead bytes/Total bytes: %s/%s (%.2f%%)' % (byte_cnt, byte_total,
byte_cnt/byte_total*100.0))
if len(sys.argv) >= 3 and sys.argv[2] == '--clobber':
clobber(sys.argv[1], to_clobber, funcs)
print('Clobbered %s functions' % len(to_clobber))
if __name__ == '__main__':
main()

17
enclave/bin/sgx-gdb Executable file
View File

@ -0,0 +1,17 @@
#!/bin/bash
for dir in build/linux-sgx-* $(dir $0)/build/linux-sgx-*; do
if [ -d "$dir/build/linux" ]; then
SGX_SDK_SOURCE_DIR="$dir"
fi
done
if [ -z "$SGX_SDK_SOURCE_DIR" ]; then
echo "please set \$SGX_SDK_SOURCE_DIR"
exit 1
fi
GDB_SGX_PLUGIN_PATH=${SGX_SDK_SOURCE_DIR}/build/linux/gdb-sgx-plugin
export PYTHONPATH=$GDB_SGX_PLUGIN_PATH
export SGX_DBG_OPTIN=1
LD_PRELOAD="${SGX_SDK_SOURCE_DIR}/build/linux/libsgx_ptrace.so" gdb -iex "directory ${GDB_SGX_PLUGIN_PATH}" -iex "source ${GDB_SGX_PLUGIN_PATH}/gdb_sgx_plugin.py" -iex "set environment LD_PRELOAD" -iex "add-auto-load-safe-path /usr/lib" "$@"

View File

View File

@ -0,0 +1,29 @@
#
# Azure Pipeline to reproducibly build the backup enclave. Triggers on tags matching enclave-*.
#
pr: none
trigger:
branches:
include:
- refs/tags/enclave-*
resources:
containers:
- container: kbupd-enclave-builder
image: signalbackupci:kbupd-enclave-builder
endpoint: signalbackupci-container-registry
options: --user 0:0
stages:
- stage: docker_build_kbupd_enclave_builder
displayName: docker build kbupd-enclave-builder
jobs:
- template: jobs/docker_build_kbupd_enclave_builder.yml
- stage: make_all
displayName: make all
dependsOn: docker_build_kbupd_enclave_builder
jobs:
- template: jobs/make_all.yml

View File

@ -0,0 +1,38 @@
#
# Azure Pipelines job to build the kbupd-enclave-builder docker image used to build the enclave
#
jobs:
- job: docker_build_kbupd_enclave_builder
displayName: docker build kbupd-enclave-builder
pool:
vmImage: ubuntu-18.04
steps:
- task: Docker@2
displayName: docker login
inputs:
command: login
containerRegistry: signalbackupci-container-registry
- script: docker pull signalbackupci.azurecr.io/signalbackupci:kbupd-enclave-builder || true
displayName: docker pull
- task: Docker@2
displayName: docker build
inputs:
command: build
dockerfile: enclave/docker/Dockerfile
arguments: --build-arg UID=1001 --build-arg GID=1001 --cache-from signalbackupci.azurecr.io/signalbackupci:kbupd-enclave-builder
repository: signalbackupci
tags: |
kbupd-enclave-builder
kbupd-enclave-builder-$(Build.SourceVersion)
- task: Docker@2
displayName: docker push
inputs:
command: push
repository: signalbackupci
tags: |
kbupd-enclave-builder
kbupd-enclave-builder-$(Build.SourceVersion)

View File

@ -0,0 +1,52 @@
#
# Azure Pipelines job to build the enclave from within in the kbupd-enclave-builder docker image.
#
jobs:
- job: make_all
displayName: make all
pool:
vmImage: ubuntu-18.04
container: kbupd-enclave-builder
timeoutInMinutes: 120
steps:
- script: |
cp -a enclave /home/signal/src && \
mkdir enclave/build/ && \
ln -s `pwd`/enclave/build /home/signal/src/build
displayName: set up build directory
- task: CacheBeta@1
displayName: cached enclave/build/bin/
inputs:
path: enclave/build/bin/
key: enclave/docker/Dockerfile | enclave/docker/apt.conf | enclave/docker/sources.list | enclave/sgx_enclave.mk | "$(Build.SourceBranch)"
restoreKeys:
enclave/docker/Dockerfile | enclave/docker/apt.conf | enclave/docker/sources.list | enclave/sgx_enclave.mk | "refs/heads/$(System.PullRequest.TargetBranch)"
enclave/docker/Dockerfile | enclave/docker/apt.conf | enclave/docker/sources.list | enclave/sgx_enclave.mk
- script: make -C /home/signal/src/ bindgen
displayName: make bindgen
- script: make -C /home/signal/src/ debuild
displayName: make debuild
- publish: enclave/build/libkbupd_enclave.unstripped.so
artifact: libkbupd_enclave.unstripped.so
- publish: enclave/build/libkbupd_enclave.hardened.unstripped.so
artifact: libkbupd_enclave.hardened.unstripped.so
- publish: enclave/build/libkbupd_enclave.hardened.unsigned.so
artifact: libkbupd_enclave.hardened.unsigned.so
- publish: enclave/build/libkbupd_enclave.hardened.signdata
artifact: libkbupd_enclave.hardened.signdata
- publish: enclave/build/libkbupd_enclave.hardened.mrenclave
artifact: libkbupd_enclave.hardened.mrenclave
- publish: enclave/build/bin/llvm-bolt
artifact: llvm-bolt
- script: make -C /home/signal/src/ tar
displayName: make tar
- publish: enclave/build/libkbupd_enclave.hardened.build.tar.bz2
artifact: libkbupd_enclave.hardened.build.tar.bz2

View File

@ -0,0 +1,13 @@
#
# Azure Pipelines job to run all enclave tests from within in the kbupd-enclave-builder docker image.
#
jobs:
- job: make_test
displayName: make test
pool:
vmImage: ubuntu-18.04
container: kbupd-enclave-builder
steps:
- script: make -C enclave/ test
displayName: make test

View File

@ -0,0 +1,44 @@
#
# Azure Pipeline to build & test the backup enclave. Triggers on commits and PRs to master in enclave/.
#
pr:
branches:
include:
- master
paths:
include:
- enclave/
trigger:
branches:
include:
- master
paths:
include:
- enclave/
resources:
containers:
- container: kbupd-enclave-builder
image: signalbackupci:kbupd-enclave-builder
endpoint: signalbackupci-container-registry
options: --user 0:0
stages:
- stage: docker_build_kbupd_enclave_builder
displayName: docker build kbupd-enclave-builder
jobs:
- template: jobs/docker_build_kbupd_enclave_builder.yml
- stage: make_test
displayName: make test
dependsOn: docker_build_kbupd_enclave_builder
jobs:
- template: jobs/make_test.yml
- stage: make_all
displayName: make all
dependsOn: docker_build_kbupd_enclave_builder
jobs:
- template: jobs/make_all.yml

209
enclave/debian/buildinfo Normal file
View File

@ -0,0 +1,209 @@
Format: 1.0
Source: kbupd-enclave
Binary: kbupd-enclave
Architecture: amd64
Version: 1.0
Checksums-Md5:
59ef215ccc0d2628535a2a92d45ec15d 583248 kbupd-enclave_1.0_amd64.deb
Checksums-Sha1:
e7ece57ea7b04ced54dbe4edb1d1d0d3eae1760d 583248 kbupd-enclave_1.0_amd64.deb
Checksums-Sha256:
27fa91826311e79327241f0f067a819ebdaf1f8713ed33e19fa78362a457d218 583248 kbupd-enclave_1.0_amd64.deb
Build-Origin: Debian
Build-Architecture: amd64
Build-Date: Mon, 18 Nov 2019 00:18:32 +0000
Installed-Build-Depends:
autoconf (= 2.69-11),
automake (= 1:1.16.1-4),
autopoint (= 0.19.8.1-9),
autotools-dev (= 20180224.1),
base-files (= 10.3+deb10u1),
base-passwd (= 3.5.46),
bash (= 5.0-4),
binutils (= 2.31.1-16),
binutils-common (= 2.31.1-16),
binutils-x86-64-linux-gnu (= 2.31.1-16),
bsdmainutils (= 11.1.2+b1),
bsdutils (= 1:2.33.1-0.1),
build-essential (= 12.6),
bzip2 (= 1.0.6-9.2~deb10u1),
clang-7 (= 1:7.0.1-8),
coreutils (= 8.30-3),
cpp (= 4:8.3.0-1),
cpp-8 (= 8.3.0-6),
curl (= 7.64.0-4),
dash (= 0.5.10.2-5),
debconf (= 1.5.71),
debhelper (= 12.1.1),
debianutils (= 4.8.6.1),
dh-autoreconf (= 19),
dh-strip-nondeterminism (= 1.1.2-1),
diffutils (= 1:3.7-3),
dpkg (= 1.19.7),
dpkg-dev (= 1.19.7),
dwz (= 0.12-3),
fdisk (= 2.33.1-0.1),
file (= 1:5.35-4),
findutils (= 4.6.0+git+20190209-2),
g++ (= 4:8.3.0-1),
g++-8 (= 8.3.0-6),
gcc (= 4:8.3.0-1),
gcc-8 (= 8.3.0-6),
gcc-8-base (= 8.3.0-6),
gettext (= 0.19.8.1-9),
gettext-base (= 0.19.8.1-9),
git (= 1:2.20.1-2),
git-man (= 1:2.20.1-2),
grep (= 3.3-1),
groff-base (= 1.22.4-3),
gzip (= 1.9-3),
hostname (= 3.21),
init-system-helpers (= 1.56+nmu1),
intltool-debian (= 0.35.0+20060710.5),
lib32gcc1 (= 1:8.3.0-6),
lib32stdc++6 (= 8.3.0-6),
libacl1 (= 2.2.53-4),
libarchive-zip-perl (= 1.64-1),
libasan5 (= 8.3.0-6),
libatomic1 (= 8.3.0-6),
libattr1 (= 1:2.4.48-4),
libaudit-common (= 1:2.8.4-3),
libaudit1 (= 1:2.8.4-3),
libbinutils (= 2.31.1-16),
libblkid1 (= 2.33.1-0.1),
libbsd0 (= 0.9.1-2),
libbz2-1.0 (= 1.0.6-9.2~deb10u1),
libc-bin (= 2.28-10),
libc-dev-bin (= 2.28-10),
libc6 (= 2.28-10),
libc6-dev (= 2.28-10),
libc6-i386 (= 2.28-10),
libcap-ng0 (= 0.7.9-2),
libcc1-0 (= 8.3.0-6),
libclang-common-7-dev (= 1:7.0.1-8),
libclang1-7 (= 1:7.0.1-8),
libcom-err2 (= 1.44.5-1+deb10u2),
libcroco3 (= 0.6.12-3),
libcurl3-gnutls (= 7.64.0-4),
libcurl4 (= 7.64.0-4),
libcurl4-openssl-dev (= 7.64.0-4),
libdb5.3 (= 5.3.28+dfsg1-0.5),
libdebconfclient0 (= 0.249),
libdpkg-perl (= 1.19.7),
libedit2 (= 3.1-20181209-1),
libelf1 (= 0.176-1.1),
liberror-perl (= 0.17027-2),
libexpat1 (= 2.2.6-2+deb10u1),
libfdisk1 (= 2.33.1-0.1),
libffi6 (= 3.2.1-9),
libfile-stripnondeterminism-perl (= 1.1.2-1),
libgc1c2 (= 1:7.6.4-0.4),
libgcc-8-dev (= 8.3.0-6),
libgcc1 (= 1:8.3.0-6),
libgcrypt20 (= 1.8.4-5),
libgdbm-compat4 (= 1.18.1-4),
libgdbm6 (= 1.18.1-4),
libglib2.0-0 (= 2.58.3-2+deb10u1),
libgmp10 (= 2:6.1.2+dfsg-4),
libgnutls30 (= 3.6.7-4),
libgomp1 (= 8.3.0-6),
libgpg-error0 (= 1.35-1),
libgssapi-krb5-2 (= 1.17-3),
libhogweed4 (= 3.4.1-1),
libicu63 (= 63.1-6),
libidn2-0 (= 2.0.5-1),
libisl19 (= 0.20-2),
libitm1 (= 8.3.0-6),
libk5crypto3 (= 1.17-3),
libkeyutils1 (= 1.6-6),
libkrb5-3 (= 1.17-3),
libkrb5support0 (= 1.17-3),
libldap-2.4-2 (= 2.4.47+dfsg-3+deb10u1),
libldap-common (= 2.4.47+dfsg-3+deb10u1),
libllvm7 (= 1:7.0.1-8),
liblsan0 (= 8.3.0-6),
liblz4-1 (= 1.8.3-1),
liblzma5 (= 5.2.4-1),
libmagic-mgc (= 1:5.35-4),
libmagic1 (= 1:5.35-4),
libmount1 (= 2.33.1-0.1),
libmpc3 (= 1.1.0-1),
libmpfr6 (= 4.0.2-1),
libmpx2 (= 8.3.0-6),
libncurses-dev (= 6.1+20181013-2+deb10u1),
libncurses5-dev (= 6.1+20181013-2+deb10u1),
libncurses6 (= 6.1+20181013-2+deb10u1),
libncursesw6 (= 6.1+20181013-2+deb10u1),
libnettle6 (= 3.4.1-1),
libnghttp2-14 (= 1.36.0-2+deb10u1),
libobjc-8-dev (= 8.3.0-6),
libobjc4 (= 8.3.0-6),
libp11-kit0 (= 0.23.15-2),
libpam-modules (= 1.3.1-5),
libpam-modules-bin (= 1.3.1-5),
libpam-runtime (= 1.3.1-5),
libpam0g (= 1.3.1-5),
libpcre2-8-0 (= 10.32-5),
libpcre3 (= 2:8.39-12),
libperl5.28 (= 5.28.1-6),
libpipeline1 (= 1.5.1-2),
libprotobuf-dev (= 3.6.1.3-2),
libprotobuf-lite17 (= 3.6.1.3-2),
libprotobuf17 (= 3.6.1.3-2),
libpsl5 (= 0.20.2-2),
libquadmath0 (= 8.3.0-6),
librtmp1 (= 2.4+20151223.gitfa8646d.1-2),
libsasl2-2 (= 2.1.27+dfsg-1),
libsasl2-modules-db (= 2.1.27+dfsg-1),
libseccomp2 (= 2.3.3-4),
libselinux1 (= 2.8-1+b1),
libsigsegv2 (= 2.12-2),
libsmartcols1 (= 2.33.1-0.1),
libssh2-1 (= 1.8.0-2.1),
libssl-dev (= 1.1.1d-0+deb10u2),
libssl1.1 (= 1.1.1d-0+deb10u2),
libstdc++-8-dev (= 8.3.0-6),
libstdc++6 (= 8.3.0-6),
libsystemd0 (= 241-7~deb10u1),
libtasn1-6 (= 4.13-3),
libtinfo6 (= 6.1+20181013-2+deb10u1),
libtool (= 2.4.6-9),
libtsan0 (= 8.3.0-6),
libubsan1 (= 8.3.0-6),
libuchardet0 (= 0.0.6-3),
libudev1 (= 241-7~deb10u1),
libunistring2 (= 0.9.10-1),
libuuid1 (= 2.33.1-0.1),
libxml2 (= 2.9.4+dfsg1-7+b3),
linux-libc-dev (= 4.19.67-2+deb10u1),
login (= 1:4.5-1.1),
m4 (= 1.4.18-2),
make (= 4.2.1-1.2),
man-db (= 2.8.5-2),
mawk (= 1.3.3-17+b3),
ncurses-base (= 6.1+20181013-2+deb10u1),
ncurses-bin (= 6.1+20181013-2+deb10u1),
ocaml-base-nox (= 4.05.0-11),
ocaml-compiler-libs (= 4.05.0-11),
ocaml-interp (= 4.05.0-11),
ocaml-nox (= 4.05.0-11),
patch (= 2.7.6-3+deb10u1),
perl (= 5.28.1-6),
perl-base (= 5.28.1-6),
perl-modules-5.28 (= 5.28.1-6),
pkg-config (= 0.29-6),
po-debconf (= 1.0.21),
sed (= 4.7-1),
sensible-utils (= 0.0.12),
sysvinit-utils (= 2.93-8),
tar (= 1.30+dfsg-6),
util-linux (= 2.33.1-0.1),
wget (= 1.20.1-1.1),
xz-utils (= 5.2.4-1),
zlib1g (= 1:1.2.11.dfsg-1),
zlib1g-dev (= 1:1.2.11.dfsg-1)
Environment:
DEB_BUILD_OPTIONS="parallel=1"
LC_ALL="C"
MAKEFLAGS=" -j1"
SOURCE_DATE_EPOCH="1543475532"

5
enclave/debian/changelog Normal file
View File

@ -0,0 +1,5 @@
kbupd-enclave (1.0) unstable; urgency=medium
*
-- Jeffrey Griffin <jeff@signal.org> Wed, 28 Nov 2018 23:12:12 -0800

1
enclave/debian/compat Normal file
View File

@ -0,0 +1 @@
10

25
enclave/debian/control Normal file
View File

@ -0,0 +1,25 @@
Source: kbupd-enclave
Section: database
Priority: optional
Maintainer: Jeffrey Griffin <jeff@signal.org>
Build-Depends:
debhelper (>= 10),
automake,
autoconf,
libtool,
curl,
wget,
git,
ocaml-native-compilers,
pkg-config,
libssl-dev,
libprotobuf-dev,
libcurl4-openssl-dev,
Standards-Version: 4.0.0
Package: kbupd-enclave
Architecture: amd64
Depends:
${misc:Depends},
Description: Key Backup Service Enclave library
Signal's Key Backup Service Enclave library

668
enclave/debian/copyright Normal file
View File

@ -0,0 +1,668 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: kbupd-enclave
Source: <https://github.com/signalapp/KeyBackupService>
Files: *
Copyright: Copyright (C) 2018 Open Whisper Systems
License: AGPL-3+
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<http://www.gnu.org/licenses/>.

16
enclave/debian/rules Executable file
View File

@ -0,0 +1,16 @@
#!/usr/bin/make -f
export DH_VERBOSE = 1
export DEB_BUILD_MAINT_OPTIONS=qa=-all sanitize=-all hardening=-all reproducible=-all parallel=1
unexport LANG
export LC_ALL=C
%:
dh $@ --no-parallel
override_dh_auto_build:
dh_auto_build -- debuild-kbupd-enclave-build
override_dh_auto_install:
make DESTDIR=debian/kbupd-enclave debuild-kbupd-enclave-install
override_dh_auto_test:
dh_auto_build -- debuild-kbupd-enclave-test

View File

@ -0,0 +1 @@
3.0 (native)

View File

@ -0,0 +1,2 @@
shared-lib-without-dependency-information usr/lib/kbupd/enclave/*.so
shlib-with-executable-stack usr/lib/kbupd/enclave/*.so

42
enclave/docker/Dockerfile Normal file
View File

@ -0,0 +1,42 @@
FROM debian:buster-20191014
COPY apt.conf sources.list /etc/apt/
RUN apt-get update \
&& apt-get install -V -y --no-install-recommends --allow-downgrades \
build-essential ca-certificates curl git cmake ninja-build \
devscripts debhelper fakeroot libwww-perl gnupg \
ocaml-native-compilers ocamlbuild automake autoconf libtool wget python pkg-config \
libssl-dev libcurl4-openssl-dev protobuf-compiler libprotobuf-dev \
llvm-dev libclang-dev clang \
&& rm -rf /var/lib/apt/lists/*
ARG UID
ARG GID
#Create a user to map the host user to.
RUN groupadd -o -g ${GID} signal \
&& useradd -m -o -u ${UID} -g ${GID} -s /bin/bash signal \
&& mkdir -p /tmp/docker \
&& chown -R signal.signal /tmp/docker
USER signal
ENV HOME /home/signal
ENV USER signal
ENV SHELL /bin/bash
WORKDIR /home/signal
ARG TOOLCHAIN=1.37.0
COPY rustup-init.sha256 /tmp/docker/
RUN curl -f https://static.rust-lang.org/rustup/archive/1.20.2/x86_64-unknown-linux-gnu/rustup-init -o /tmp/rustup-init \
&& [ `sha256sum /tmp/rustup-init|cut -d' ' -f1` = `cut -d' ' -f1</tmp/docker/rustup-init.sha256` ] \
&& chmod a+x /tmp/rustup-init \
&& /tmp/rustup-init -y --profile minimal --component rustfmt --default-toolchain "${TOOLCHAIN}" \
&& rm -rf /tmp/rustup-init /tmp/docker
ENV PATH="/home/signal/.cargo/bin:${PATH}"
CMD [ "/bin/bash" ]

10
enclave/docker/apt.conf Normal file
View File

@ -0,0 +1,10 @@
Apt {
Architecture "amd64";
Architectures "amd64";
};
Acquire::Check-Valid-Until "false";
Acquire::Languages "none";
Binary::apt-get::Acquire::AllowInsecureRepositories "false";
APT::Install-Recommends "false";

View File

@ -0,0 +1 @@
e68f193542c68ce83c449809d2cad262cc2bbb99640eb47c58fc1dc58cc30add *target/x86_64-unknown-linux-gnu/release/rustup-init

View File

@ -0,0 +1,2 @@
deb http://snapshot.debian.org/archive/debian/20191014T000000Z/ buster main
deb http://snapshot.debian.org/archive/debian-security/20191014T000000Z/ buster/updates main

View File

@ -0,0 +1,170 @@
/*
* Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef BR_BEARSSL_H__
#define BR_BEARSSL_H__
#include <stddef.h>
#include <stdint.h>
/** \mainpage BearSSL API
*
* # API Layout
*
* The functions and structures defined by the BearSSL API are located
* in various header files:
*
* | Header file | Elements |
* | :-------------- | :------------------------------------------------ |
* | bearssl_hash.h | Hash functions |
* | bearssl_hmac.h | HMAC |
* | bearssl_kdf.h | Key Derivation Functions |
* | bearssl_rand.h | Pseudorandom byte generators |
* | bearssl_prf.h | PRF implementations (for SSL/TLS) |
* | bearssl_block.h | Symmetric encryption |
* | bearssl_aead.h | AEAD algorithms (combined encryption + MAC) |
* | bearssl_rsa.h | RSA encryption and signatures |
* | bearssl_ec.h | Elliptic curves support (including ECDSA) |
* | bearssl_ssl.h | SSL/TLS engine interface |
* | bearssl_x509.h | X.509 certificate decoding and validation |
* | bearssl_pem.h | Base64/PEM decoding support functions |
*
* Applications using BearSSL are supposed to simply include `bearssl.h`
* as follows:
*
* #include <bearssl.h>
*
* The `bearssl.h` file itself includes all the other header files. It is
* possible to include specific header files, but it has no practical
* advantage for the application. The API is separated into separate
* header files only for documentation convenience.
*
*
* # Conventions
*
* ## MUST and SHALL
*
* In all descriptions, the usual "MUST", "SHALL", "MAY",... terminology
* is used. Failure to meet requirements expressed with a "MUST" or
* "SHALL" implies undefined behaviour, which means that segmentation
* faults, buffer overflows, and other similar adverse events, may occur.
*
* In general, BearSSL is not very forgiving of programming errors, and
* does not include much failsafes or error reporting when the problem
* does not arise from external transient conditions, and can be fixed
* only in the application code. This is done so in order to make the
* total code footprint lighter.
*
*
* ## `NULL` values
*
* Function parameters with a pointer type shall not be `NULL` unless
* explicitly authorised by the documentation. As an exception, when
* the pointer aims at a sequence of bytes and is accompanied with
* a length parameter, and the length is zero (meaning that there is
* no byte at all to retrieve), then the pointer may be `NULL` even if
* not explicitly allowed.
*
*
* ## Memory Allocation
*
* BearSSL does not perform dynamic memory allocation. This implies that
* for any functionality that requires a non-transient state, the caller
* is responsible for allocating the relevant context structure. Such
* allocation can be done in any appropriate area, including static data
* segments, the heap, and the stack, provided that proper alignment is
* respected. The header files define these context structures
* (including size and contents), so the C compiler should handle
* alignment automatically.
*
* Since there is no dynamic resource allocation, there is also nothing to
* release. When the calling code is done with a BearSSL feature, it
* may simple release the context structures it allocated itself, with
* no "close function" to call. If the context structures were allocated
* on the stack (as local variables), then even that release operation is
* implicit.
*
*
* ## Structure Contents
*
* Except when explicitly indicated, structure contents are opaque: they
* are included in the header files so that calling code may know the
* structure sizes and alignment requirements, but callers SHALL NOT
* access individual fields directly. For fields that are supposed to
* be read from or written to, the API defines accessor functions (the
* simplest of these accessor functions are defined as `static inline`
* functions, and the C compiler will optimise them away).
*
*
* # API Usage
*
* BearSSL usage for running a SSL/TLS client or server is described
* on the [BearSSL Web site](https://www.bearssl.org/api1.html). The
* BearSSL source archive also comes with sample code.
*/
#include "bearssl_hash.h"
#include "bearssl_hmac.h"
#include "bearssl_kdf.h"
#include "bearssl_rand.h"
#include "bearssl_prf.h"
#include "bearssl_block.h"
#include "bearssl_aead.h"
#include "bearssl_rsa.h"
#include "bearssl_ec.h"
#include "bearssl_ssl.h"
#include "bearssl_x509.h"
#include "bearssl_pem.h"
/** \brief Type for a configuration option.
*
* A "configuration option" is a value that is selected when the BearSSL
* library itself is compiled. Most options are boolean; their value is
* then either 1 (option is enabled) or 0 (option is disabled). Some
* values have other integer values. Option names correspond to macro
* names. Some of the options can be explicitly set in the internal
* `"config.h"` file.
*/
typedef struct {
/** \brief Configurable option name. */
const char *name;
/** \brief Configurable option value. */
long value;
} br_config_option;
/** \brief Get configuration report.
*
* This function returns compiled configuration options, each as a
* 'long' value. Names match internal macro names, in particular those
* that can be set in the `"config.h"` inner file. For boolean options,
* the numerical value is 1 if enabled, 0 if disabled. For maximum
* key sizes, values are expressed in bits.
*
* The returned array is terminated by an entry whose `name` is `NULL`.
*
* \return the configuration report.
*/
const br_config_option *br_get_config(void);
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,967 @@
/*
* Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef BR_BEARSSL_EC_H__
#define BR_BEARSSL_EC_H__
#include <stddef.h>
#include <stdint.h>
#include "bearssl_rand.h"
#ifdef __cplusplus
extern "C" {
#endif
/** \file bearssl_ec.h
*
* # Elliptic Curves
*
* This file documents the EC implementations provided with BearSSL, and
* ECDSA.
*
* ## Elliptic Curve API
*
* Only "named curves" are supported. Each EC implementation supports
* one or several named curves, identified by symbolic identifiers.
* These identifiers are small integers, that correspond to the values
* registered by the
* [IANA](http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8).
*
* Since all currently defined elliptic curve identifiers are in the 0..31
* range, it is convenient to encode support of some curves in a 32-bit
* word, such that bit x corresponds to curve of identifier x.
*
* An EC implementation is incarnated by a `br_ec_impl` instance, that
* offers the following fields:
*
* - `supported_curves`
*
* A 32-bit word that documents the identifiers of the curves supported
* by this implementation.
*
* - `generator()`
*
* Callback method that returns a pointer to the conventional generator
* point for that curve.
*
* - `order()`
*
* Callback method that returns a pointer to the subgroup order for
* that curve. That value uses unsigned big-endian encoding.
*
* - `xoff()`
*
* Callback method that returns the offset and length of the X
* coordinate in an encoded point.
*
* - `mul()`
*
* Multiply a curve point with an integer.
*
* - `mulgen()`
*
* Multiply the curve generator with an integer. This may be faster
* than the generic `mul()`.
*
* - `muladd()`
*
* Multiply two curve points by two integers, and return the sum of
* the two products.
*
* All curve points are represented in uncompressed format. The `mul()`
* and `muladd()` methods take care to validate that the provided points
* are really part of the relevant curve subgroup.
*
* For all point multiplication functions, the following holds:
*
* - Functions validate that the provided points are valid members
* of the relevant curve subgroup. An error is reported if that is
* not the case.
*
* - Processing is constant-time, even if the point operands are not
* valid. This holds for both the source and resulting points, and
* the multipliers (integers). Only the byte length of the provided
* multiplier arrays (not their actual value length in bits) may
* leak through timing-based side channels.
*
* - The multipliers (integers) MUST be lower than the subgroup order.
* If this property is not met, then the result is indeterminate,
* but an error value is not ncessearily returned.
*
*
* ## ECDSA
*
* ECDSA signatures have two standard formats, called "raw" and "asn1".
* Internally, such a signature is a pair of modular integers `(r,s)`.
* The "raw" format is the concatenation of the unsigned big-endian
* encodings of these two integers, possibly left-padded with zeros so
* that they have the same encoded length. The "asn1" format is the
* DER encoding of an ASN.1 structure that contains the two integer
* values:
*
* ECDSASignature ::= SEQUENCE {
* r INTEGER,
* s INTEGER
* }
*
* In general, in all of X.509 and SSL/TLS, the "asn1" format is used.
* BearSSL offers ECDSA implementations for both formats; conversion
* functions between the two formats are also provided. Conversion of a
* "raw" format signature into "asn1" may enlarge a signature by no more
* than 9 bytes for all supported curves; conversely, conversion of an
* "asn1" signature to "raw" may expand the signature but the "raw"
* length will never be more than twice the length of the "asn1" length
* (and usually it will be shorter).
*
* Note that for a given signature, the "raw" format is not fully
* deterministic, in that it does not enforce a minimal common length.
*/
/*
* Standard curve ID. These ID are equal to the assigned numerical
* identifiers assigned to these curves for TLS:
* http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8
*/
/** \brief Identifier for named curve sect163k1. */
#define BR_EC_sect163k1 1
/** \brief Identifier for named curve sect163r1. */
#define BR_EC_sect163r1 2
/** \brief Identifier for named curve sect163r2. */
#define BR_EC_sect163r2 3
/** \brief Identifier for named curve sect193r1. */
#define BR_EC_sect193r1 4
/** \brief Identifier for named curve sect193r2. */
#define BR_EC_sect193r2 5
/** \brief Identifier for named curve sect233k1. */
#define BR_EC_sect233k1 6
/** \brief Identifier for named curve sect233r1. */
#define BR_EC_sect233r1 7
/** \brief Identifier for named curve sect239k1. */
#define BR_EC_sect239k1 8
/** \brief Identifier for named curve sect283k1. */
#define BR_EC_sect283k1 9
/** \brief Identifier for named curve sect283r1. */
#define BR_EC_sect283r1 10
/** \brief Identifier for named curve sect409k1. */
#define BR_EC_sect409k1 11
/** \brief Identifier for named curve sect409r1. */
#define BR_EC_sect409r1 12
/** \brief Identifier for named curve sect571k1. */
#define BR_EC_sect571k1 13
/** \brief Identifier for named curve sect571r1. */
#define BR_EC_sect571r1 14
/** \brief Identifier for named curve secp160k1. */
#define BR_EC_secp160k1 15
/** \brief Identifier for named curve secp160r1. */
#define BR_EC_secp160r1 16
/** \brief Identifier for named curve secp160r2. */
#define BR_EC_secp160r2 17
/** \brief Identifier for named curve secp192k1. */
#define BR_EC_secp192k1 18
/** \brief Identifier for named curve secp192r1. */
#define BR_EC_secp192r1 19
/** \brief Identifier for named curve secp224k1. */
#define BR_EC_secp224k1 20
/** \brief Identifier for named curve secp224r1. */
#define BR_EC_secp224r1 21
/** \brief Identifier for named curve secp256k1. */
#define BR_EC_secp256k1 22
/** \brief Identifier for named curve secp256r1. */
#define BR_EC_secp256r1 23
/** \brief Identifier for named curve secp384r1. */
#define BR_EC_secp384r1 24
/** \brief Identifier for named curve secp521r1. */
#define BR_EC_secp521r1 25
/** \brief Identifier for named curve brainpoolP256r1. */
#define BR_EC_brainpoolP256r1 26
/** \brief Identifier for named curve brainpoolP384r1. */
#define BR_EC_brainpoolP384r1 27
/** \brief Identifier for named curve brainpoolP512r1. */
#define BR_EC_brainpoolP512r1 28
/** \brief Identifier for named curve Curve25519. */
#define BR_EC_curve25519 29
/** \brief Identifier for named curve Curve448. */
#define BR_EC_curve448 30
/**
* \brief Structure for an EC public key.
*/
typedef struct {
/** \brief Identifier for the curve used by this key. */
int curve;
/** \brief Public curve point (uncompressed format). */
unsigned char *q;
/** \brief Length of public curve point (in bytes). */
size_t qlen;
} br_ec_public_key;
/**
* \brief Structure for an EC private key.
*
* The private key is an integer modulo the curve subgroup order. The
* encoding below tolerates extra leading zeros. In general, it is
* recommended that the private key has the same length as the curve
* subgroup order.
*/
typedef struct {
/** \brief Identifier for the curve used by this key. */
int curve;
/** \brief Private key (integer, unsigned big-endian encoding). */
unsigned char *x;
/** \brief Private key length (in bytes). */
size_t xlen;
} br_ec_private_key;
/**
* \brief Type for an EC implementation.
*/
typedef struct {
/**
* \brief Supported curves.
*
* This word is a bitfield: bit `x` is set if the curve of ID `x`
* is supported. E.g. an implementation supporting both NIST P-256
* (secp256r1, ID 23) and NIST P-384 (secp384r1, ID 24) will have
* value `0x01800000` in this field.
*/
uint32_t supported_curves;
/**
* \brief Get the conventional generator.
*
* This function returns the conventional generator (encoded
* curve point) for the specified curve. This function MUST NOT
* be called if the curve is not supported.
*
* \param curve curve identifier.
* \param len receiver for the encoded generator length (in bytes).
* \return the encoded generator.
*/
const unsigned char *(*generator)(int curve, size_t *len);
/**
* \brief Get the subgroup order.
*
* This function returns the order of the subgroup generated by
* the conventional generator, for the specified curve. Unsigned
* big-endian encoding is used. This function MUST NOT be called
* if the curve is not supported.
*
* \param curve curve identifier.
* \param len receiver for the encoded order length (in bytes).
* \return the encoded order.
*/
const unsigned char *(*order)(int curve, size_t *len);
/**
* \brief Get the offset and length for the X coordinate.
*
* This function returns the offset and length (in bytes) of
* the X coordinate in an encoded non-zero point.
*
* \param curve curve identifier.
* \param len receiver for the X coordinate length (in bytes).
* \return the offset for the X coordinate (in bytes).
*/
size_t (*xoff)(int curve, size_t *len);
/**
* \brief Multiply a curve point by an integer.
*
* The source point is provided in array `G` (of size `Glen` bytes);
* the multiplication result is written over it. The multiplier
* `x` (of size `xlen` bytes) uses unsigned big-endian encoding.
*
* Rules:
*
* - The specified curve MUST be supported.
*
* - The source point must be a valid point on the relevant curve
* subgroup (and not the "point at infinity" either). If this is
* not the case, then this function returns an error (0).
*
* - The multiplier integer MUST be non-zero and less than the
* curve subgroup order. If this property does not hold, then
* the result is indeterminate and an error code is not
* guaranteed.
*
* Returned value is 1 on success, 0 on error. On error, the
* contents of `G` are indeterminate.
*
* \param G point to multiply.
* \param Glen length of the encoded point (in bytes).
* \param x multiplier (unsigned big-endian).
* \param xlen multiplier length (in bytes).
* \param curve curve identifier.
* \return 1 on success, 0 on error.
*/
uint32_t (*mul)(unsigned char *G, size_t Glen,
const unsigned char *x, size_t xlen, int curve);
/**
* \brief Multiply the generator by an integer.
*
* The multiplier MUST be non-zero and less than the curve
* subgroup order. Results are indeterminate if this property
* does not hold.
*
* \param R output buffer for the point.
* \param x multiplier (unsigned big-endian).
* \param xlen multiplier length (in bytes).
* \param curve curve identifier.
* \return encoded result point length (in bytes).
*/
size_t (*mulgen)(unsigned char *R,
const unsigned char *x, size_t xlen, int curve);
/**
* \brief Multiply two points by two integers and add the
* results.
*
* The point `x*A + y*B` is computed and written back in the `A`
* array.
*
* Rules:
*
* - The specified curve MUST be supported.
*
* - The source points (`A` and `B`) must be valid points on
* the relevant curve subgroup (and not the "point at
* infinity" either). If this is not the case, then this
* function returns an error (0).
*
* - If the `B` pointer is `NULL`, then the conventional
* subgroup generator is used. With some implementations,
* this may be faster than providing a pointer to the
* generator.
*
* - The multiplier integers (`x` and `y`) MUST be non-zero
* and less than the curve subgroup order. If either integer
* is zero, then an error is reported, but if one of them is
* not lower than the subgroup order, then the result is
* indeterminate and an error code is not guaranteed.
*
* - If the final result is the point at infinity, then an
* error is returned.
*
* Returned value is 1 on success, 0 on error. On error, the
* contents of `A` are indeterminate.
*
* \param A first point to multiply.
* \param B second point to multiply (`NULL` for the generator).
* \param len common length of the encoded points (in bytes).
* \param x multiplier for `A` (unsigned big-endian).
* \param xlen length of multiplier for `A` (in bytes).
* \param y multiplier for `A` (unsigned big-endian).
* \param ylen length of multiplier for `A` (in bytes).
* \param curve curve identifier.
* \return 1 on success, 0 on error.
*/
uint32_t (*muladd)(unsigned char *A, const unsigned char *B, size_t len,
const unsigned char *x, size_t xlen,
const unsigned char *y, size_t ylen, int curve);
} br_ec_impl;
/**
* \brief EC implementation "i31".
*
* This implementation internally uses generic code for modular integers,
* with a representation as sequences of 31-bit words. It supports secp256r1,
* secp384r1 and secp521r1 (aka NIST curves P-256, P-384 and P-521).
*/
extern const br_ec_impl br_ec_prime_i31;
/**
* \brief EC implementation "i15".
*
* This implementation internally uses generic code for modular integers,
* with a representation as sequences of 15-bit words. It supports secp256r1,
* secp384r1 and secp521r1 (aka NIST curves P-256, P-384 and P-521).
*/
extern const br_ec_impl br_ec_prime_i15;
/**
* \brief EC implementation "m15" for P-256.
*
* This implementation uses specialised code for curve secp256r1 (also
* known as NIST P-256), with optional Karatsuba decomposition, and fast
* modular reduction thanks to the field modulus special format. Only
* 32-bit multiplications are used (with 32-bit results, not 64-bit).
*/
extern const br_ec_impl br_ec_p256_m15;
/**
* \brief EC implementation "m31" for P-256.
*
* This implementation uses specialised code for curve secp256r1 (also
* known as NIST P-256), relying on multiplications of 31-bit values
* (MUL31).
*/
extern const br_ec_impl br_ec_p256_m31;
/**
* \brief EC implementation "m62" (specialised code) for P-256.
*
* This implementation uses custom code relying on multiplication of
* integers up to 64 bits, with a 128-bit result. This implementation is
* defined only on platforms that offer the 64x64->128 multiplication
* support; use `br_ec_p256_m62_get()` to dynamically obtain a pointer
* to that implementation.
*/
extern const br_ec_impl br_ec_p256_m62;
/**
* \brief Get the "m62" implementation of P-256, if available.
*
* \return the implementation, or 0.
*/
const br_ec_impl *br_ec_p256_m62_get(void);
/**
* \brief EC implementation "m64" (specialised code) for P-256.
*
* This implementation uses custom code relying on multiplication of
* integers up to 64 bits, with a 128-bit result. This implementation is
* defined only on platforms that offer the 64x64->128 multiplication
* support; use `br_ec_p256_m64_get()` to dynamically obtain a pointer
* to that implementation.
*/
extern const br_ec_impl br_ec_p256_m64;
/**
* \brief Get the "m64" implementation of P-256, if available.
*
* \return the implementation, or 0.
*/
const br_ec_impl *br_ec_p256_m64_get(void);
/**
* \brief EC implementation "i15" (generic code) for Curve25519.
*
* This implementation uses the generic code for modular integers (with
* 15-bit words) to support Curve25519. Due to the specificities of the
* curve definition, the following applies:
*
* - `muladd()` is not implemented (the function returns 0 systematically).
* - `order()` returns 2^255-1, since the point multiplication algorithm
* accepts any 32-bit integer as input (it clears the top bit and low
* three bits systematically).
*/
extern const br_ec_impl br_ec_c25519_i15;
/**
* \brief EC implementation "i31" (generic code) for Curve25519.
*
* This implementation uses the generic code for modular integers (with
* 31-bit words) to support Curve25519. Due to the specificities of the
* curve definition, the following applies:
*
* - `muladd()` is not implemented (the function returns 0 systematically).
* - `order()` returns 2^255-1, since the point multiplication algorithm
* accepts any 32-bit integer as input (it clears the top bit and low
* three bits systematically).
*/
extern const br_ec_impl br_ec_c25519_i31;
/**
* \brief EC implementation "m15" (specialised code) for Curve25519.
*
* This implementation uses custom code relying on multiplication of
* integers up to 15 bits. Due to the specificities of the curve
* definition, the following applies:
*
* - `muladd()` is not implemented (the function returns 0 systematically).
* - `order()` returns 2^255-1, since the point multiplication algorithm
* accepts any 32-bit integer as input (it clears the top bit and low
* three bits systematically).
*/
extern const br_ec_impl br_ec_c25519_m15;
/**
* \brief EC implementation "m31" (specialised code) for Curve25519.
*
* This implementation uses custom code relying on multiplication of
* integers up to 31 bits. Due to the specificities of the curve
* definition, the following applies:
*
* - `muladd()` is not implemented (the function returns 0 systematically).
* - `order()` returns 2^255-1, since the point multiplication algorithm
* accepts any 32-bit integer as input (it clears the top bit and low
* three bits systematically).
*/
extern const br_ec_impl br_ec_c25519_m31;
/**
* \brief EC implementation "m62" (specialised code) for Curve25519.
*
* This implementation uses custom code relying on multiplication of
* integers up to 62 bits, with a 124-bit result. This implementation is
* defined only on platforms that offer the 64x64->128 multiplication
* support; use `br_ec_c25519_m62_get()` to dynamically obtain a pointer
* to that implementation. Due to the specificities of the curve
* definition, the following applies:
*
* - `muladd()` is not implemented (the function returns 0 systematically).
* - `order()` returns 2^255-1, since the point multiplication algorithm
* accepts any 32-bit integer as input (it clears the top bit and low
* three bits systematically).
*/
extern const br_ec_impl br_ec_c25519_m62;
/**
* \brief Get the "m62" implementation of Curve25519, if available.
*
* \return the implementation, or 0.
*/
const br_ec_impl *br_ec_c25519_m62_get(void);
/**
* \brief EC implementation "m64" (specialised code) for Curve25519.
*
* This implementation uses custom code relying on multiplication of
* integers up to 64 bits, with a 128-bit result. This implementation is
* defined only on platforms that offer the 64x64->128 multiplication
* support; use `br_ec_c25519_m64_get()` to dynamically obtain a pointer
* to that implementation. Due to the specificities of the curve
* definition, the following applies:
*
* - `muladd()` is not implemented (the function returns 0 systematically).
* - `order()` returns 2^255-1, since the point multiplication algorithm
* accepts any 32-bit integer as input (it clears the top bit and low
* three bits systematically).
*/
extern const br_ec_impl br_ec_c25519_m64;
/**
* \brief Get the "m64" implementation of Curve25519, if available.
*
* \return the implementation, or 0.
*/
const br_ec_impl *br_ec_c25519_m64_get(void);
/**
* \brief Aggregate EC implementation "m15".
*
* This implementation is a wrapper for:
*
* - `br_ec_c25519_m15` for Curve25519
* - `br_ec_p256_m15` for NIST P-256
* - `br_ec_prime_i15` for other curves (NIST P-384 and NIST-P512)
*/
extern const br_ec_impl br_ec_all_m15;
/**
* \brief Aggregate EC implementation "m31".
*
* This implementation is a wrapper for:
*
* - `br_ec_c25519_m31` for Curve25519
* - `br_ec_p256_m31` for NIST P-256
* - `br_ec_prime_i31` for other curves (NIST P-384 and NIST-P512)
*/
extern const br_ec_impl br_ec_all_m31;
/**
* \brief Get the "default" EC implementation for the current system.
*
* This returns a pointer to the preferred implementation on the
* current system.
*
* \return the default EC implementation.
*/
const br_ec_impl *br_ec_get_default(void);
/**
* \brief Convert a signature from "raw" to "asn1".
*
* Conversion is done "in place" and the new length is returned.
* Conversion may enlarge the signature, but by no more than 9 bytes at
* most. On error, 0 is returned (error conditions include an odd raw
* signature length, or an oversized integer).
*
* \param sig signature to convert.
* \param sig_len signature length (in bytes).
* \return the new signature length, or 0 on error.
*/
size_t br_ecdsa_raw_to_asn1(void *sig, size_t sig_len);
/**
* \brief Convert a signature from "asn1" to "raw".
*
* Conversion is done "in place" and the new length is returned.
* Conversion may enlarge the signature, but the new signature length
* will be less than twice the source length at most. On error, 0 is
* returned (error conditions include an invalid ASN.1 structure or an
* oversized integer).
*
* \param sig signature to convert.
* \param sig_len signature length (in bytes).
* \return the new signature length, or 0 on error.
*/
size_t br_ecdsa_asn1_to_raw(void *sig, size_t sig_len);
/**
* \brief Type for an ECDSA signer function.
*
* A pointer to the EC implementation is provided. The hash value is
* assumed to have the length inferred from the designated hash function
* class.
*
* Signature is written in the buffer pointed to by `sig`, and the length
* (in bytes) is returned. On error, nothing is written in the buffer,
* and 0 is returned. This function returns 0 if the specified curve is
* not supported by the provided EC implementation.
*
* The signature format is either "raw" or "asn1", depending on the
* implementation; maximum length is predictable from the implemented
* curve:
*
* | curve | raw | asn1 |
* | :--------- | --: | ---: |
* | NIST P-256 | 64 | 72 |
* | NIST P-384 | 96 | 104 |
* | NIST P-521 | 132 | 139 |
*
* \param impl EC implementation to use.
* \param hf hash function used to process the data.
* \param hash_value signed data (hashed).
* \param sk EC private key.
* \param sig destination buffer.
* \return the signature length (in bytes), or 0 on error.
*/
typedef size_t (*br_ecdsa_sign)(const br_ec_impl *impl,
const br_hash_class *hf, const void *hash_value,
const br_ec_private_key *sk, void *sig);
/**
* \brief Type for an ECDSA signature verification function.
*
* A pointer to the EC implementation is provided. The hashed value,
* computed over the purportedly signed data, is also provided with
* its length.
*
* The signature format is either "raw" or "asn1", depending on the
* implementation.
*
* Returned value is 1 on success (valid signature), 0 on error. This
* function returns 0 if the specified curve is not supported by the
* provided EC implementation.
*
* \param impl EC implementation to use.
* \param hash signed data (hashed).
* \param hash_len hash value length (in bytes).
* \param pk EC public key.
* \param sig signature.
* \param sig_len signature length (in bytes).
* \return 1 on success, 0 on error.
*/
typedef uint32_t (*br_ecdsa_vrfy)(const br_ec_impl *impl,
const void *hash, size_t hash_len,
const br_ec_public_key *pk, const void *sig, size_t sig_len);
/**
* \brief ECDSA signature generator, "i31" implementation, "asn1" format.
*
* \see br_ecdsa_sign()
*
* \param impl EC implementation to use.
* \param hf hash function used to process the data.
* \param hash_value signed data (hashed).
* \param sk EC private key.
* \param sig destination buffer.
* \return the signature length (in bytes), or 0 on error.
*/
size_t br_ecdsa_i31_sign_asn1(const br_ec_impl *impl,
const br_hash_class *hf, const void *hash_value,
const br_ec_private_key *sk, void *sig);
/**
* \brief ECDSA signature generator, "i31" implementation, "raw" format.
*
* \see br_ecdsa_sign()
*
* \param impl EC implementation to use.
* \param hf hash function used to process the data.
* \param hash_value signed data (hashed).
* \param sk EC private key.
* \param sig destination buffer.
* \return the signature length (in bytes), or 0 on error.
*/
size_t br_ecdsa_i31_sign_raw(const br_ec_impl *impl,
const br_hash_class *hf, const void *hash_value,
const br_ec_private_key *sk, void *sig);
/**
* \brief ECDSA signature verifier, "i31" implementation, "asn1" format.
*
* \see br_ecdsa_vrfy()
*
* \param impl EC implementation to use.
* \param hash signed data (hashed).
* \param hash_len hash value length (in bytes).
* \param pk EC public key.
* \param sig signature.
* \param sig_len signature length (in bytes).
* \return 1 on success, 0 on error.
*/
uint32_t br_ecdsa_i31_vrfy_asn1(const br_ec_impl *impl,
const void *hash, size_t hash_len,
const br_ec_public_key *pk, const void *sig, size_t sig_len);
/**
* \brief ECDSA signature verifier, "i31" implementation, "raw" format.
*
* \see br_ecdsa_vrfy()
*
* \param impl EC implementation to use.
* \param hash signed data (hashed).
* \param hash_len hash value length (in bytes).
* \param pk EC public key.
* \param sig signature.
* \param sig_len signature length (in bytes).
* \return 1 on success, 0 on error.
*/
uint32_t br_ecdsa_i31_vrfy_raw(const br_ec_impl *impl,
const void *hash, size_t hash_len,
const br_ec_public_key *pk, const void *sig, size_t sig_len);
/**
* \brief ECDSA signature generator, "i15" implementation, "asn1" format.
*
* \see br_ecdsa_sign()
*
* \param impl EC implementation to use.
* \param hf hash function used to process the data.
* \param hash_value signed data (hashed).
* \param sk EC private key.
* \param sig destination buffer.
* \return the signature length (in bytes), or 0 on error.
*/
size_t br_ecdsa_i15_sign_asn1(const br_ec_impl *impl,
const br_hash_class *hf, const void *hash_value,
const br_ec_private_key *sk, void *sig);
/**
* \brief ECDSA signature generator, "i15" implementation, "raw" format.
*
* \see br_ecdsa_sign()
*
* \param impl EC implementation to use.
* \param hf hash function used to process the data.
* \param hash_value signed data (hashed).
* \param sk EC private key.
* \param sig destination buffer.
* \return the signature length (in bytes), or 0 on error.
*/
size_t br_ecdsa_i15_sign_raw(const br_ec_impl *impl,
const br_hash_class *hf, const void *hash_value,
const br_ec_private_key *sk, void *sig);
/**
* \brief ECDSA signature verifier, "i15" implementation, "asn1" format.
*
* \see br_ecdsa_vrfy()
*
* \param impl EC implementation to use.
* \param hash signed data (hashed).
* \param hash_len hash value length (in bytes).
* \param pk EC public key.
* \param sig signature.
* \param sig_len signature length (in bytes).
* \return 1 on success, 0 on error.
*/
uint32_t br_ecdsa_i15_vrfy_asn1(const br_ec_impl *impl,
const void *hash, size_t hash_len,
const br_ec_public_key *pk, const void *sig, size_t sig_len);
/**
* \brief ECDSA signature verifier, "i15" implementation, "raw" format.
*
* \see br_ecdsa_vrfy()
*
* \param impl EC implementation to use.
* \param hash signed data (hashed).
* \param hash_len hash value length (in bytes).
* \param pk EC public key.
* \param sig signature.
* \param sig_len signature length (in bytes).
* \return 1 on success, 0 on error.
*/
uint32_t br_ecdsa_i15_vrfy_raw(const br_ec_impl *impl,
const void *hash, size_t hash_len,
const br_ec_public_key *pk, const void *sig, size_t sig_len);
/**
* \brief Get "default" ECDSA implementation (signer, asn1 format).
*
* This returns the preferred implementation of ECDSA signature generation
* ("asn1" output format) on the current system.
*
* \return the default implementation.
*/
br_ecdsa_sign br_ecdsa_sign_asn1_get_default(void);
/**
* \brief Get "default" ECDSA implementation (signer, raw format).
*
* This returns the preferred implementation of ECDSA signature generation
* ("raw" output format) on the current system.
*
* \return the default implementation.
*/
br_ecdsa_sign br_ecdsa_sign_raw_get_default(void);
/**
* \brief Get "default" ECDSA implementation (verifier, asn1 format).
*
* This returns the preferred implementation of ECDSA signature verification
* ("asn1" output format) on the current system.
*
* \return the default implementation.
*/
br_ecdsa_vrfy br_ecdsa_vrfy_asn1_get_default(void);
/**
* \brief Get "default" ECDSA implementation (verifier, raw format).
*
* This returns the preferred implementation of ECDSA signature verification
* ("raw" output format) on the current system.
*
* \return the default implementation.
*/
br_ecdsa_vrfy br_ecdsa_vrfy_raw_get_default(void);
/**
* \brief Maximum size for EC private key element buffer.
*
* This is the largest number of bytes that `br_ec_keygen()` may need or
* ever return.
*/
#define BR_EC_KBUF_PRIV_MAX_SIZE 72
/**
* \brief Maximum size for EC public key element buffer.
*
* This is the largest number of bytes that `br_ec_compute_public()` may
* need or ever return.
*/
#define BR_EC_KBUF_PUB_MAX_SIZE 145
/**
* \brief Generate a new EC private key.
*
* If the specified `curve` is not supported by the elliptic curve
* implementation (`impl`), then this function returns zero.
*
* The `sk` structure fields are set to the new private key data. In
* particular, `sk.x` is made to point to the provided key buffer (`kbuf`),
* in which the actual private key data is written. That buffer is assumed
* to be large enough. The `BR_EC_KBUF_PRIV_MAX_SIZE` defines the maximum
* size for all supported curves.
*
* The number of bytes used in `kbuf` is returned. If `kbuf` is `NULL`, then
* the private key is not actually generated, and `sk` may also be `NULL`;
* the minimum length for `kbuf` is still computed and returned.
*
* If `sk` is `NULL` but `kbuf` is not `NULL`, then the private key is
* still generated and stored in `kbuf`.
*
* \param rng_ctx source PRNG context (already initialized).
* \param impl the elliptic curve implementation.
* \param sk the private key structure to fill, or `NULL`.
* \param kbuf the key element buffer, or `NULL`.
* \param curve the curve identifier.
* \return the key data length (in bytes), or zero.
*/
size_t br_ec_keygen(const br_prng_class **rng_ctx,
const br_ec_impl *impl, br_ec_private_key *sk,
void *kbuf, int curve);
/**
* \brief Compute EC public key from EC private key.
*
* This function uses the provided elliptic curve implementation (`impl`)
* to compute the public key corresponding to the private key held in `sk`.
* The public key point is written into `kbuf`, which is then linked from
* the `*pk` structure. The size of the public key point, i.e. the number
* of bytes used in `kbuf`, is returned.
*
* If `kbuf` is `NULL`, then the public key point is NOT computed, and
* the public key structure `*pk` is unmodified (`pk` may be `NULL` in
* that case). The size of the public key point is still returned.
*
* If `pk` is `NULL` but `kbuf` is not `NULL`, then the public key
* point is computed and stored in `kbuf`, and its size is returned.
*
* If the curve used by the private key is not supported by the curve
* implementation, then this function returns zero.
*
* The private key MUST be valid. An off-range private key value is not
* necessarily detected, and leads to unpredictable results.
*
* \param impl the elliptic curve implementation.
* \param pk the public key structure to fill (or `NULL`).
* \param kbuf the public key point buffer (or `NULL`).
* \param sk the source private key.
* \return the public key point length (in bytes), or zero.
*/
size_t br_ec_compute_pub(const br_ec_impl *impl, br_ec_public_key *pk,
void *kbuf, const br_ec_private_key *sk);
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,241 @@
/*
* Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef BR_BEARSSL_HMAC_H__
#define BR_BEARSSL_HMAC_H__
#include <stddef.h>
#include <stdint.h>
#include "bearssl_hash.h"
#ifdef __cplusplus
extern "C" {
#endif
/** \file bearssl_hmac.h
*
* # HMAC
*
* HMAC is initialized with a key and an underlying hash function; it
* then fills a "key context". That context contains the processed
* key.
*
* With the key context, a HMAC context can be initialized to process
* the input bytes and obtain the MAC output. The key context is not
* modified during that process, and can be reused.
*
* IMPORTANT: HMAC shall be used only with functions that have the
* following properties:
*
* - hash output size does not exceed 64 bytes;
* - hash internal state size does not exceed 64 bytes;
* - internal block length is a power of 2 between 16 and 256 bytes.
*/
/**
* \brief HMAC key context.
*
* The HMAC key context is initialised with a hash function implementation
* and a secret key. Contents are opaque (callers should not access them
* directly). The caller is responsible for allocating the context where
* appropriate. Context initialisation and usage incurs no dynamic
* allocation, so there is no release function.
*/
typedef struct {
#ifndef BR_DOXYGEN_IGNORE
const br_hash_class *dig_vtable;
unsigned char ksi[64], kso[64];
#endif
} br_hmac_key_context;
/**
* \brief HMAC key context initialisation.
*
* Initialise the key context with the provided key, using the hash function
* identified by `digest_vtable`. This supports arbitrary key lengths.
*
* \param kc HMAC key context to initialise.
* \param digest_vtable pointer to the hash function implementation vtable.
* \param key pointer to the HMAC secret key.
* \param key_len HMAC secret key length (in bytes).
*/
void br_hmac_key_init(br_hmac_key_context *kc,
const br_hash_class *digest_vtable, const void *key, size_t key_len);
/*
* \brief Get the underlying hash function.
*
* This function returns a pointer to the implementation vtable of the
* hash function used for this HMAC key context.
*
* \param kc HMAC key context.
* \return the hash function implementation.
*/
static inline const br_hash_class *br_hmac_key_get_digest(
const br_hmac_key_context *kc)
{
return kc->dig_vtable;
}
/**
* \brief HMAC computation context.
*
* The HMAC computation context maintains the state for a single HMAC
* computation. It is modified as input bytes are injected. The context
* is caller-allocated and has no release function since it does not
* dynamically allocate external resources. Its contents are opaque.
*/
typedef struct {
#ifndef BR_DOXYGEN_IGNORE
br_hash_compat_context dig;
unsigned char kso[64];
size_t out_len;
#endif
} br_hmac_context;
/**
* \brief HMAC computation initialisation.
*
* Initialise a HMAC context with a key context. The key context is
* unmodified. Relevant data from the key context is immediately copied;
* the key context can thus be independently reused, modified or released
* without impacting this HMAC computation.
*
* An explicit output length can be specified; the actual output length
* will be the minimum of that value and the natural HMAC output length.
* If `out_len` is 0, then the natural HMAC output length is selected. The
* "natural output length" is the output length of the underlying hash
* function.
*
* \param ctx HMAC context to initialise.
* \param kc HMAC key context (already initialised with the key).
* \param out_len HMAC output length (0 to select "natural length").
*/
void br_hmac_init(br_hmac_context *ctx,
const br_hmac_key_context *kc, size_t out_len);
/**
* \brief Get the HMAC output size.
*
* The HMAC output size is the number of bytes that will actually be
* produced with `br_hmac_out()` with the provided context. This function
* MUST NOT be called on a non-initialised HMAC computation context.
* The returned value is the minimum of the HMAC natural length (output
* size of the underlying hash function) and the `out_len` parameter which
* was used with the last `br_hmac_init()` call on that context (if the
* initialisation `out_len` parameter was 0, then this function will
* return the HMAC natural length).
*
* \param ctx the (already initialised) HMAC computation context.
* \return the HMAC actual output size.
*/
static inline size_t
br_hmac_size(br_hmac_context *ctx)
{
return ctx->out_len;
}
/*
* \brief Get the underlying hash function.
*
* This function returns a pointer to the implementation vtable of the
* hash function used for this HMAC context.
*
* \param hc HMAC context.
* \return the hash function implementation.
*/
static inline const br_hash_class *br_hmac_get_digest(
const br_hmac_context *hc)
{
return hc->dig.vtable;
}
/**
* \brief Inject some bytes in HMAC.
*
* The provided `len` bytes are injected as extra input in the HMAC
* computation incarnated by the `ctx` HMAC context. It is acceptable
* that `len` is zero, in which case `data` is ignored (and may be
* `NULL`) and this function does nothing.
*/
void br_hmac_update(br_hmac_context *ctx, const void *data, size_t len);
/**
* \brief Compute the HMAC output.
*
* The destination buffer MUST be large enough to accommodate the result;
* its length is at most the "natural length" of HMAC (i.e. the output
* length of the underlying hash function). The context is NOT modified;
* further bytes may be processed. Thus, "partial HMAC" values can be
* efficiently obtained.
*
* Returned value is the output length (in bytes).
*
* \param ctx HMAC computation context.
* \param out destination buffer for the HMAC output.
* \return the produced value length (in bytes).
*/
size_t br_hmac_out(const br_hmac_context *ctx, void *out);
/**
* \brief Constant-time HMAC computation.
*
* This function compute the HMAC output in constant time. Some extra
* input bytes are processed, then the output is computed. The extra
* input consists in the `len` bytes pointed to by `data`. The `len`
* parameter must lie between `min_len` and `max_len` (inclusive);
* `max_len` bytes are actually read from `data`. Computing time (and
* memory access pattern) will not depend upon the data byte contents or
* the value of `len`.
*
* The output is written in the `out` buffer, that MUST be large enough
* to receive it.
*
* The difference `max_len - min_len` MUST be less than 2<sup>30</sup>
* (i.e. about one gigabyte).
*
* This function computes the output properly only if the underlying
* hash function uses MD padding (i.e. MD5, SHA-1, SHA-224, SHA-256,
* SHA-384 or SHA-512).
*
* The provided context is NOT modified.
*
* \param ctx the (already initialised) HMAC computation context.
* \param data the extra input bytes.
* \param len the extra input length (in bytes).
* \param min_len minimum extra input length (in bytes).
* \param max_len maximum extra input length (in bytes).
* \param out destination buffer for the HMAC output.
* \return the produced value length (in bytes).
*/
size_t br_hmac_outCT(const br_hmac_context *ctx,
const void *data, size_t len, size_t min_len, size_t max_len,
void *out);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,284 @@
/*
* Copyright (c) 2018 Thomas Pornin <pornin@bolet.org>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef BR_BEARSSL_KDF_H__
#define BR_BEARSSL_KDF_H__
#include <stddef.h>
#include <stdint.h>
#include "bearssl_hash.h"
#include "bearssl_hmac.h"
#ifdef __cplusplus
extern "C" {
#endif
/** \file bearssl_kdf.h
*
* # Key Derivation Functions
*
* KDF are functions that takes a variable length input, and provide a
* variable length output, meant to be used to derive subkeys from a
* master key.
*
* ## HKDF
*
* HKDF is a KDF defined by [RFC 5869](https://tools.ietf.org/html/rfc5869).
* It is based on HMAC, itself using an underlying hash function. Any
* hash function can be used, as long as it is compatible with the rules
* for the HMAC implementation (i.e. output size is 64 bytes or less, hash
* internal state size is 64 bytes or less, and the internal block length is
* a power of 2 between 16 and 256 bytes). HKDF has two phases:
*
* - HKDF-Extract: the input data in ingested, along with a "salt" value.
*
* - HKDF-Expand: the output is produced, from the result of processing
* the input and salt, and using an extra non-secret parameter called
* "info".
*
* The "salt" and "info" strings are non-secret and can be empty. Their role
* is normally to bind the input and output, respectively, to conventional
* identifiers that qualifu them within the used protocol or application.
*
* The implementation defined in this file uses the following functions:
*
* - `br_hkdf_init()`: initialize an HKDF context, with a hash function,
* and the salt. This starts the HKDF-Extract process.
*
* - `br_hkdf_inject()`: inject more input bytes. This function may be
* called repeatedly if the input data is provided by chunks.
*
* - `br_hkdf_flip()`: end the HKDF-Extract process, and start the
* HKDF-Expand process.
*
* - `br_hkdf_produce()`: get the next bytes of output. This function
* may be called several times to obtain the full output by chunks.
* For correct HKDF processing, the same "info" string must be
* provided for each call.
*
* Note that the HKDF total output size (the number of bytes that
* HKDF-Expand is willing to produce) is limited: if the hash output size
* is _n_ bytes, then the maximum output size is _255*n_.
*
* ## SHAKE
*
* SHAKE is defined in
* [FIPS 202](https://csrc.nist.gov/publications/detail/fips/202/final)
* under two versions: SHAKE128 and SHAKE256, offering an alleged
* "security level" of 128 and 256 bits, respectively (SHAKE128 is
* about 20 to 25% faster than SHAKE256). SHAKE internally relies on
* the Keccak family of sponge functions, not on any externally provided
* hash function. Contrary to HKDF, SHAKE does not have a concept of
* either a "salt" or an "info" string. The API consists in four
* functions:
*
* - `br_shake_init()`: initialize a SHAKE context for a given
* security level.
*
* - `br_shake_inject()`: inject more input bytes. This function may be
* called repeatedly if the input data is provided by chunks.
*
* - `br_shake_flip()`: end the data injection process, and start the
* data production process.
*
* - `br_shake_produce()`: get the next bytes of output. This function
* may be called several times to obtain the full output by chunks.
*/
/**
* \brief HKDF context.
*
* The HKDF context is initialized with a hash function implementation
* and a salt value. Contents are opaque (callers should not access them
* directly). The caller is responsible for allocating the context where
* appropriate. Context initialisation and usage incurs no dynamic
* allocation, so there is no release function.
*/
typedef struct {
#ifndef BR_DOXYGEN_IGNORE
union {
br_hmac_context hmac_ctx;
br_hmac_key_context prk_ctx;
} u;
unsigned char buf[64];
size_t ptr;
size_t dig_len;
unsigned chunk_num;
#endif
} br_hkdf_context;
/**
* \brief HKDF context initialization.
*
* The underlying hash function and salt value are provided. Arbitrary
* salt lengths can be used.
*
* HKDF makes a difference between a salt of length zero, and an
* absent salt (the latter being equivalent to a salt consisting of
* bytes of value zero, of the same length as the hash function output).
* If `salt_len` is zero, then this function assumes that the salt is
* present but of length zero. To specify an _absent_ salt, use
* `BR_HKDF_NO_SALT` as `salt` parameter (`salt_len` is then ignored).
*
* \param hc HKDF context to initialise.
* \param digest_vtable pointer to the hash function implementation vtable.
* \param salt HKDF-Extract salt.
* \param salt_len HKDF-Extract salt length (in bytes).
*/
void br_hkdf_init(br_hkdf_context *hc, const br_hash_class *digest_vtable,
const void *salt, size_t salt_len);
/**
* \brief The special "absent salt" value for HKDF.
*/
#define BR_HKDF_NO_SALT (&br_hkdf_no_salt)
#ifndef BR_DOXYGEN_IGNORE
extern const unsigned char br_hkdf_no_salt;
#endif
/**
* \brief HKDF input injection (HKDF-Extract).
*
* This function injects some more input bytes ("key material") into
* HKDF. This function may be called several times, after `br_hkdf_init()`
* but before `br_hkdf_flip()`.
*
* \param hc HKDF context.
* \param ikm extra input bytes.
* \param ikm_len number of extra input bytes.
*/
void br_hkdf_inject(br_hkdf_context *hc, const void *ikm, size_t ikm_len);
/**
* \brief HKDF switch to the HKDF-Expand phase.
*
* This call terminates the HKDF-Extract process (input injection), and
* starts the HKDF-Expand process (output production).
*
* \param hc HKDF context.
*/
void br_hkdf_flip(br_hkdf_context *hc);
/**
* \brief HKDF output production (HKDF-Expand).
*
* Produce more output bytes from the current state. This function may be
* called several times, but only after `br_hkdf_flip()`.
*
* Returned value is the number of actually produced bytes. The total
* output length is limited to 255 times the output length of the
* underlying hash function.
*
* \param hc HKDF context.
* \param info application specific information string.
* \param info_len application specific information string length (in bytes).
* \param out destination buffer for the HKDF output.
* \param out_len the length of the requested output (in bytes).
* \return the produced output length (in bytes).
*/
size_t br_hkdf_produce(br_hkdf_context *hc,
const void *info, size_t info_len, void *out, size_t out_len);
/**
* \brief SHAKE context.
*
* The HKDF context is initialized with a "security level". The internal
* notion is called "capacity"; the capacity is twice the security level
* (for instance, SHAKE128 has capacity 256).
*
* The caller is responsible for allocating the context where
* appropriate. Context initialisation and usage incurs no dynamic
* allocation, so there is no release function.
*/
typedef struct {
#ifndef BR_DOXYGEN_IGNORE
unsigned char dbuf[200];
size_t dptr;
size_t rate;
uint64_t A[25];
#endif
} br_shake_context;
/**
* \brief SHAKE context initialization.
*
* The context is initialized for the provided "security level".
* Internally, this sets the "capacity" to twice the security level;
* thus, for SHAKE128, the `security_level` parameter should be 128,
* which corresponds to a 256-bit capacity.
*
* Allowed security levels are all multiples of 32, from 32 to 768,
* inclusive. Larger security levels imply lower performance; levels
* beyond 256 bits don't make much sense. Standard levels are 128
* and 256 bits (for SHAKE128 and SHAKE256, respectively).
*
* \param sc SHAKE context to initialise.
* \param security_level security level (in bits).
*/
void br_shake_init(br_shake_context *sc, int security_level);
/**
* \brief SHAKE input injection.
*
* This function injects some more input bytes ("key material") into
* SHAKE. This function may be called several times, after `br_shake_init()`
* but before `br_shake_flip()`.
*
* \param sc SHAKE context.
* \param data extra input bytes.
* \param len number of extra input bytes.
*/
void br_shake_inject(br_shake_context *sc, const void *data, size_t len);
/**
* \brief SHAKE switch to production phase.
*
* This call terminates the input injection process, and starts the
* output production process.
*
* \param sc SHAKE context.
*/
void br_shake_flip(br_shake_context *hc);
/**
* \brief SHAKE output production.
*
* Produce more output bytes from the current state. This function may be
* called several times, but only after `br_shake_flip()`.
*
* There is no practical limit to the number of bytes that may be produced.
*
* \param sc SHAKE context.
* \param out destination buffer for the SHAKE output.
* \param len the length of the requested output (in bytes).
*/
void br_shake_produce(br_shake_context *sc, void *out, size_t len);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,294 @@
/*
* Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef BR_BEARSSL_PEM_H__
#define BR_BEARSSL_PEM_H__
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/** \file bearssl_pem.h
*
* # PEM Support
*
* PEM is a traditional encoding layer use to store binary objects (in
* particular X.509 certificates, and private keys) in text files. While
* the acronym comes from an old, defunct standard ("Privacy Enhanced
* Mail"), the format has been reused, with some variations, by many
* systems, and is a _de facto_ standard, even though it is not, actually,
* specified in all clarity anywhere.
*
* ## Format Details
*
* BearSSL contains a generic, streamed PEM decoder, which handles the
* following format:
*
* - The input source (a sequence of bytes) is assumed to be the
* encoding of a text file in an ASCII-compatible charset. This
* includes ISO-8859-1, Windows-1252, and UTF-8 encodings. Each
* line ends on a newline character (U+000A LINE FEED). The
* U+000D CARRIAGE RETURN characters are ignored, so the code
* accepts both Windows-style and Unix-style line endings.
*
* - Each object begins with a banner that occurs at the start of
* a line; the first banner characters are "`-----BEGIN `" (five
* dashes, the word "BEGIN", and a space). The banner matching is
* not case-sensitive.
*
* - The _object name_ consists in the characters that follow the
* banner start sequence, up to the end of the line, but without
* trailing dashes (in "normal" PEM, there are five trailing
* dashes, but this implementation is not picky about these dashes).
* The BearSSL decoder normalises the name characters to uppercase
* (for ASCII letters only) and accepts names up to 127 characters.
*
* - The object ends with a banner that again occurs at the start of
* a line, and starts with "`-----END `" (again case-insensitive).
*
* - Between that start and end banner, only Base64 data shall occur.
* Base64 converts each sequence of three bytes into four
* characters; the four characters are ASCII letters, digits, "`+`"
* or "`-`" signs, and one or two "`=`" signs may occur in the last
* quartet. Whitespace is ignored (whitespace is any ASCII character
* of code 32 or less, so control characters are whitespace) and
* lines may have arbitrary length; the only restriction is that the
* four characters of a quartet must appear on the same line (no
* line break inside a quartet).
*
* - A single file may contain more than one PEM object. Bytes that
* occur between objects are ignored.
*
*
* ## PEM Decoder API
*
* The PEM decoder offers a state-machine API. The caller allocates a
* decoder context, then injects source bytes. Source bytes are pushed
* with `br_pem_decoder_push()`. The decoder stops accepting bytes when
* it reaches an "event", which is either the start of an object, the
* end of an object, or a decoding error within an object.
*
* The `br_pem_decoder_event()` function is used to obtain the current
* event; it also clears it, thus allowing the decoder to accept more
* bytes. When a object start event is raised, the decoder context
* offers the found object name (normalised to ASCII uppercase).
*
* When an object is reached, the caller must set an appropriate callback
* function, which will receive (by chunks) the decoded object data.
*
* Since the decoder context makes no dynamic allocation, it requires
* no explicit deallocation.
*/
/**
* \brief PEM decoder context.
*
* Contents are opaque (they should not be accessed directly).
*/
typedef struct {
#ifndef BR_DOXYGEN_IGNORE
/* CPU for the T0 virtual machine. */
struct {
uint32_t *dp;
uint32_t *rp;
const unsigned char *ip;
} cpu;
uint32_t dp_stack[32];
uint32_t rp_stack[32];
int err;
const unsigned char *hbuf;
size_t hlen;
void (*dest)(void *dest_ctx, const void *src, size_t len);
void *dest_ctx;
unsigned char event;
char name[128];
unsigned char buf[255];
size_t ptr;
#endif
} br_pem_decoder_context;
/**
* \brief Initialise a PEM decoder structure.
*
* \param ctx decoder context to initialise.
*/
void br_pem_decoder_init(br_pem_decoder_context *ctx);
/**
* \brief Push some bytes into the decoder.
*
* Returned value is the number of bytes actually consumed; this may be
* less than the number of provided bytes if an event is raised. When an
* event is raised, it must be read (with `br_pem_decoder_event()`);
* until the event is read, this function will return 0.
*
* \param ctx decoder context.
* \param data new data bytes.
* \param len number of new data bytes.
* \return the number of bytes actually received (may be less than `len`).
*/
size_t br_pem_decoder_push(br_pem_decoder_context *ctx,
const void *data, size_t len);
/**
* \brief Set the receiver for decoded data.
*
* When an object is entered, the provided function (with opaque context
* pointer) will be called repeatedly with successive chunks of decoded
* data for that object. If `dest` is set to 0, then decoded data is
* simply ignored. The receiver can be set at any time, but, in practice,
* it should be called immediately after receiving a "start of object"
* event.
*
* \param ctx decoder context.
* \param dest callback for receiving decoded data.
* \param dest_ctx opaque context pointer for the `dest` callback.
*/
static inline void
br_pem_decoder_setdest(br_pem_decoder_context *ctx,
void (*dest)(void *dest_ctx, const void *src, size_t len),
void *dest_ctx)
{
ctx->dest = dest;
ctx->dest_ctx = dest_ctx;
}
/**
* \brief Get the last event.
*
* If an event was raised, then this function returns the event value, and
* also clears it, thereby allowing the decoder to proceed. If no event
* was raised since the last call to `br_pem_decoder_event()`, then this
* function returns 0.
*
* \param ctx decoder context.
* \return the raised event, or 0.
*/
int br_pem_decoder_event(br_pem_decoder_context *ctx);
/**
* \brief Event: start of object.
*
* This event is raised when the start of a new object has been detected.
* The object name (normalised to uppercase) can be accessed with
* `br_pem_decoder_name()`.
*/
#define BR_PEM_BEGIN_OBJ 1
/**
* \brief Event: end of object.
*
* This event is raised when the end of the current object is reached
* (normally, i.e. with no decoding error).
*/
#define BR_PEM_END_OBJ 2
/**
* \brief Event: decoding error.
*
* This event is raised when decoding fails within an object.
* This formally closes the current object and brings the decoder back
* to the "out of any object" state. The offending line in the source
* is consumed.
*/
#define BR_PEM_ERROR 3
/**
* \brief Get the name of the encountered object.
*
* The encountered object name is defined only when the "start of object"
* event is raised. That name is normalised to uppercase (for ASCII letters
* only) and does not include trailing dashes.
*
* \param ctx decoder context.
* \return the current object name.
*/
static inline const char *
br_pem_decoder_name(br_pem_decoder_context *ctx)
{
return ctx->name;
}
/**
* \brief Encode an object in PEM.
*
* This function encodes the provided binary object (`data`, of length `len`
* bytes) into PEM. The `banner` text will be included in the header and
* footer (e.g. use `"CERTIFICATE"` to get a `"BEGIN CERTIFICATE"` header).
*
* The length (in characters) of the PEM output is returned; that length
* does NOT include the terminating zero, that this function nevertheless
* adds. If using the returned value for allocation purposes, the allocated
* buffer size MUST be at least one byte larger than the returned size.
*
* If `dest` is `NULL`, then the encoding does not happen; however, the
* length of the encoded object is still computed and returned.
*
* The `data` pointer may be `NULL` only if `len` is zero (when encoding
* an object of length zero, which is not very useful), or when `dest`
* is `NULL` (in that case, source data bytes are ignored).
*
* Some `flags` can be specified to alter the encoding behaviour:
*
* - If `BR_PEM_LINE64` is set, then line-breaking will occur after
* every 64 characters of output, instead of the default of 76.
*
* - If `BR_PEM_CRLF` is set, then end-of-line sequence will use
* CR+LF instead of a single LF.
*
* The `data` and `dest` buffers may overlap, in which case the source
* binary data is destroyed in the process. Note that the PEM-encoded output
* is always larger than the source binary.
*
* \param dest the destination buffer (or `NULL`).
* \param data the source buffer (can be `NULL` in some cases).
* \param len the source length (in bytes).
* \param banner the PEM banner expression.
* \param flags the behavioural flags.
* \return the PEM object length (in characters), EXCLUDING the final zero.
*/
size_t br_pem_encode(void *dest, const void *data, size_t len,
const char *banner, unsigned flags);
/**
* \brief PEM encoding flag: split lines at 64 characters.
*/
#define BR_PEM_LINE64 0x0001
/**
* \brief PEM encoding flag: use CR+LF line endings.
*/
#define BR_PEM_CRLF 0x0002
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,150 @@
/*
* Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef BR_BEARSSL_PRF_H__
#define BR_BEARSSL_PRF_H__
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/** \file bearssl_prf.h
*
* # The TLS PRF
*
* The "PRF" is the pseudorandom function used internally during the
* SSL/TLS handshake, notably to expand negotiated shared secrets into
* the symmetric encryption keys that will be used to process the
* application data.
*
* TLS 1.0 and 1.1 define a PRF that is based on both MD5 and SHA-1. This
* is implemented by the `br_tls10_prf()` function.
*
* TLS 1.2 redefines the PRF, using an explicit hash function. The
* `br_tls12_sha256_prf()` and `br_tls12_sha384_prf()` functions apply that
* PRF with, respectively, SHA-256 and SHA-384. Most standard cipher suites
* rely on the SHA-256 based PRF, but some use SHA-384.
*
* The PRF always uses as input three parameters: a "secret" (some
* bytes), a "label" (ASCII string), and a "seed" (again some bytes). An
* arbitrary output length can be produced. The "seed" is provided as an
* arbitrary number of binary chunks, that gets internally concatenated.
*/
/**
* \brief Type for a seed chunk.
*
* Each chunk may have an arbitrary length, and may be empty (no byte at
* all). If the chunk length is zero, then the pointer to the chunk data
* may be `NULL`.
*/
typedef struct {
/**
* \brief Pointer to the chunk data.
*/
const void *data;
/**
* \brief Chunk length (in bytes).
*/
size_t len;
} br_tls_prf_seed_chunk;
/**
* \brief PRF implementation for TLS 1.0 and 1.1.
*
* This PRF is the one specified by TLS 1.0 and 1.1. It internally uses
* MD5 and SHA-1.
*
* \param dst destination buffer.
* \param len output length (in bytes).
* \param secret secret value (key) for this computation.
* \param secret_len length of "secret" (in bytes).
* \param label PRF label (zero-terminated ASCII string).
* \param seed_num number of seed chunks.
* \param seed seed chnks for this computation (usually non-secret).
*/
void br_tls10_prf(void *dst, size_t len,
const void *secret, size_t secret_len, const char *label,
size_t seed_num, const br_tls_prf_seed_chunk *seed);
/**
* \brief PRF implementation for TLS 1.2, with SHA-256.
*
* This PRF is the one specified by TLS 1.2, when the underlying hash
* function is SHA-256.
*
* \param dst destination buffer.
* \param len output length (in bytes).
* \param secret secret value (key) for this computation.
* \param secret_len length of "secret" (in bytes).
* \param label PRF label (zero-terminated ASCII string).
* \param seed_num number of seed chunks.
* \param seed seed chnks for this computation (usually non-secret).
*/
void br_tls12_sha256_prf(void *dst, size_t len,
const void *secret, size_t secret_len, const char *label,
size_t seed_num, const br_tls_prf_seed_chunk *seed);
/**
* \brief PRF implementation for TLS 1.2, with SHA-384.
*
* This PRF is the one specified by TLS 1.2, when the underlying hash
* function is SHA-384.
*
* \param dst destination buffer.
* \param len output length (in bytes).
* \param secret secret value (key) for this computation.
* \param secret_len length of "secret" (in bytes).
* \param label PRF label (zero-terminated ASCII string).
* \param seed_num number of seed chunks.
* \param seed seed chnks for this computation (usually non-secret).
*/
void br_tls12_sha384_prf(void *dst, size_t len,
const void *secret, size_t secret_len, const char *label,
size_t seed_num, const br_tls_prf_seed_chunk *seed);
/**
* brief A convenient type name for a PRF implementation.
*
* \param dst destination buffer.
* \param len output length (in bytes).
* \param secret secret value (key) for this computation.
* \param secret_len length of "secret" (in bytes).
* \param label PRF label (zero-terminated ASCII string).
* \param seed_num number of seed chunks.
* \param seed seed chnks for this computation (usually non-secret).
*/
typedef void (*br_tls_prf_impl)(void *dst, size_t len,
const void *secret, size_t secret_len, const char *label,
size_t seed_num, const br_tls_prf_seed_chunk *seed);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,397 @@
/*
* Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#ifndef BR_BEARSSL_RAND_H__
#define BR_BEARSSL_RAND_H__
#include <stddef.h>
#include <stdint.h>
#include "bearssl_block.h"
#include "bearssl_hash.h"
#ifdef __cplusplus
extern "C" {
#endif
/** \file bearssl_rand.h
*
* # Pseudo-Random Generators
*
* A PRNG is a state-based engine that outputs pseudo-random bytes on
* demand. It is initialized with an initial seed, and additional seed
* bytes can be added afterwards. Bytes produced depend on the seeds and
* also on the exact sequence of calls (including sizes requested for
* each call).
*
*
* ## Procedural and OOP API
*
* For the PRNG of name "`xxx`", two API are provided. The _procedural_
* API defined a context structure `br_xxx_context` and three functions:
*
* - `br_xxx_init()`
*
* Initialise the context with an initial seed.
*
* - `br_xxx_generate()`
*
* Produce some pseudo-random bytes.
*
* - `br_xxx_update()`
*
* Inject some additional seed.
*
* The initialisation function sets the first context field (`vtable`)
* to a pointer to the vtable that supports the OOP API. The OOP API
* provides access to the same functions through function pointers,
* named `init()`, `generate()` and `update()`.
*
* Note that the context initialisation method may accept additional
* parameters, provided as a 'const void *' pointer at API level. These
* additional parameters depend on the implemented PRNG.
*
*
* ## HMAC_DRBG
*
* HMAC_DRBG is defined in [NIST SP 800-90A Revision
* 1](http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf).
* It uses HMAC repeatedly, over some configurable underlying hash
* function. In BearSSL, it is implemented under the "`hmac_drbg`" name.
* The "extra parameters" pointer for context initialisation should be
* set to a pointer to the vtable for the underlying hash function (e.g.
* pointer to `br_sha256_vtable` to use HMAC_DRBG with SHA-256).
*
* According to the NIST standard, each request shall produce up to
* 2<sup>19</sup> bits (i.e. 64 kB of data); moreover, the context shall
* be reseeded at least once every 2<sup>48</sup> requests. This
* implementation does not maintain the reseed counter (the threshold is
* too high to be reached in practice) and does not object to producing
* more than 64 kB in a single request; thus, the code cannot fail,
* which corresponds to the fact that the API has no room for error
* codes. However, this implies that requesting more than 64 kB in one
* `generate()` request, or making more than 2<sup>48</sup> requests
* without reseeding, is formally out of NIST specification. There is
* no currently known security penalty for exceeding the NIST limits,
* and, in any case, HMAC_DRBG usage in implementing SSL/TLS always
* stays much below these thresholds.
*
*
* ## AESCTR_DRBG
*
* AESCTR_DRBG is a custom PRNG based on AES-128 in CTR mode. This is
* meant to be used only in situations where you are desperate for
* speed, and have an hardware-optimized AES/CTR implementation. Whether
* this will yield perceptible improvements depends on what you use the
* pseudorandom bytes for, and how many you want; for instance, RSA key
* pair generation uses a substantial amount of randomness, and using
* AESCTR_DRBG instead of HMAC_DRBG yields a 15 to 20% increase in key
* generation speed on a recent x86 CPU (Intel Core i7-6567U at 3.30 GHz).
*
* Internally, it uses CTR mode with successive counter values, starting
* at zero (counter value expressed over 128 bits, big-endian convention).
* The counter is not allowed to reach 32768; thus, every 32768*16 bytes
* at most, the `update()` function is run (on an empty seed, if none is
* provided). The `update()` function computes the new AES-128 key by
* applying a custom hash function to the concatenation of a state-dependent
* word (encryption of an all-one block with the current key) and the new
* seed. The custom hash function uses Hirose's construction over AES-256;
* see the comments in `aesctr_drbg.c` for details.
*
* This DRBG does not follow an existing standard, and thus should be
* considered as inadequate for production use until it has been properly
* analysed.
*/
/**
* \brief Class type for PRNG implementations.
*
* A `br_prng_class` instance references the methods implementing a PRNG.
* Constant instances of this structure are defined for each implemented
* PRNG. Such instances are also called "vtables".
*/
typedef struct br_prng_class_ br_prng_class;
struct br_prng_class_ {
/**
* \brief Size (in bytes) of the context structure appropriate for
* running this PRNG.
*/
size_t context_size;
/**
* \brief Initialisation method.
*
* The context to initialise is provided as a pointer to its
* first field (the vtable pointer); this function sets that
* first field to a pointer to the vtable.
*
* The extra parameters depend on the implementation; each
* implementation defines what kind of extra parameters it
* expects (if any).
*
* Requirements on the initial seed depend on the implemented
* PRNG.
*
* \param ctx PRNG context to initialise.
* \param params extra parameters for the PRNG.
* \param seed initial seed.
* \param seed_len initial seed length (in bytes).
*/
void (*init)(const br_prng_class **ctx, const void *params,
const void *seed, size_t seed_len);
/**
* \brief Random bytes generation.
*
* This method produces `len` pseudorandom bytes, in the `out`
* buffer. The context is updated accordingly.
*
* \param ctx PRNG context.
* \param out output buffer.
* \param len number of pseudorandom bytes to produce.
*/
void (*generate)(const br_prng_class **ctx, void *out, size_t len);
/**
* \brief Inject additional seed bytes.
*
* The provided seed bytes are added into the PRNG internal
* entropy pool.
*
* \param ctx PRNG context.
* \param seed additional seed.
* \param seed_len additional seed length (in bytes).
*/
void (*update)(const br_prng_class **ctx,
const void *seed, size_t seed_len);
};
/**
* \brief Context for HMAC_DRBG.
*
* The context contents are opaque, except the first field, which
* supports OOP.
*/
typedef struct {
/**
* \brief Pointer to the vtable.
*
* This field is set with the initialisation method/function.
*/
const br_prng_class *vtable;
#ifndef BR_DOXYGEN_IGNORE
unsigned char K[64];
unsigned char V[64];
const br_hash_class *digest_class;
#endif
} br_hmac_drbg_context;
/**
* \brief Statically allocated, constant vtable for HMAC_DRBG.
*/
extern const br_prng_class br_hmac_drbg_vtable;
/**
* \brief HMAC_DRBG initialisation.
*
* The context to initialise is provided as a pointer to its first field
* (the vtable pointer); this function sets that first field to a
* pointer to the vtable.
*
* The `seed` value is what is called, in NIST terminology, the
* concatenation of the "seed", "nonce" and "personalization string", in
* that order.
*
* The `digest_class` parameter defines the underlying hash function.
* Formally, the NIST standard specifies that the hash function shall
* be only SHA-1 or one of the SHA-2 functions. This implementation also
* works with any other implemented hash function (such as MD5), but
* this is non-standard and therefore not recommended.
*
* \param ctx HMAC_DRBG context to initialise.
* \param digest_class vtable for the underlying hash function.
* \param seed initial seed.
* \param seed_len initial seed length (in bytes).
*/
void br_hmac_drbg_init(br_hmac_drbg_context *ctx,
const br_hash_class *digest_class, const void *seed, size_t seed_len);
/**
* \brief Random bytes generation with HMAC_DRBG.
*
* This method produces `len` pseudorandom bytes, in the `out`
* buffer. The context is updated accordingly. Formally, requesting
* more than 65536 bytes in one request falls out of specification
* limits (but it won't fail).
*
* \param ctx HMAC_DRBG context.
* \param out output buffer.
* \param len number of pseudorandom bytes to produce.
*/
void br_hmac_drbg_generate(br_hmac_drbg_context *ctx, void *out, size_t len);
/**
* \brief Inject additional seed bytes in HMAC_DRBG.
*
* The provided seed bytes are added into the HMAC_DRBG internal
* entropy pool. The process does not _replace_ existing entropy,
* thus pushing non-random bytes (i.e. bytes which are known to the
* attackers) does not degrade the overall quality of generated bytes.
*
* \param ctx HMAC_DRBG context.
* \param seed additional seed.
* \param seed_len additional seed length (in bytes).
*/
void br_hmac_drbg_update(br_hmac_drbg_context *ctx,
const void *seed, size_t seed_len);
/**
* \brief Get the hash function implementation used by a given instance of
* HMAC_DRBG.
*
* This calls MUST NOT be performed on a context which was not
* previously initialised.
*
* \param ctx HMAC_DRBG context.
* \return the hash function vtable.
*/
static inline const br_hash_class *
br_hmac_drbg_get_hash(const br_hmac_drbg_context *ctx)
{
return ctx->digest_class;
}
/**
* \brief Type for a provider of entropy seeds.
*
* A "seeder" is a function that is able to obtain random values from
* some source and inject them as entropy seed in a PRNG. A seeder
* shall guarantee that the total entropy of the injected seed is large
* enough to seed a PRNG for purposes of cryptographic key generation
* (i.e. at least 128 bits).
*
* A seeder may report a failure to obtain adequate entropy. Seeders
* shall endeavour to fix themselves transient errors by trying again;
* thus, callers may consider reported errors as permanent.
*
* \param ctx PRNG context to seed.
* \return 1 on success, 0 on error.
*/
typedef int (*br_prng_seeder)(const br_prng_class **ctx);
/**
* \brief Get a seeder backed by the operating system or hardware.
*
* Get a seeder that feeds on RNG facilities provided by the current
* operating system or hardware. If no such facility is known, then 0
* is returned.
*
* If `name` is not `NULL`, then `*name` is set to a symbolic string
* that identifies the seeder implementation. If no seeder is returned
* and `name` is not `NULL`, then `*name` is set to a pointer to the
* constant string `"none"`.
*
* \param name receiver for seeder name, or `NULL`.
* \return the system seeder, if available, or 0.
*/
br_prng_seeder br_prng_seeder_system(const char **name);
/**
* \brief Context for AESCTR_DRBG.
*
* The context contents are opaque, except the first field, which
* supports OOP.
*/
typedef struct {
/**
* \brief Pointer to the vtable.
*
* This field is set with the initialisation method/function.
*/
const br_prng_class *vtable;
#ifndef BR_DOXYGEN_IGNORE
br_aes_gen_ctr_keys sk;
uint32_t cc;
#endif
} br_aesctr_drbg_context;
/**
* \brief Statically allocated, constant vtable for AESCTR_DRBG.
*/
extern const br_prng_class br_aesctr_drbg_vtable;
/**
* \brief AESCTR_DRBG initialisation.
*
* The context to initialise is provided as a pointer to its first field
* (the vtable pointer); this function sets that first field to a
* pointer to the vtable.
*
* The internal AES key is first set to the all-zero key; then, the
* `br_aesctr_drbg_update()` function is called with the provided `seed`.
* The call is performed even if the seed length (`seed_len`) is zero.
*
* The `aesctr` parameter defines the underlying AES/CTR implementation.
*
* \param ctx AESCTR_DRBG context to initialise.
* \param aesctr vtable for the AES/CTR implementation.
* \param seed initial seed (can be `NULL` if `seed_len` is zero).
* \param seed_len initial seed length (in bytes).
*/
void br_aesctr_drbg_init(br_aesctr_drbg_context *ctx,
const br_block_ctr_class *aesctr, const void *seed, size_t seed_len);
/**
* \brief Random bytes generation with AESCTR_DRBG.
*
* This method produces `len` pseudorandom bytes, in the `out`
* buffer. The context is updated accordingly.
*
* \param ctx AESCTR_DRBG context.
* \param out output buffer.
* \param len number of pseudorandom bytes to produce.
*/
void br_aesctr_drbg_generate(br_aesctr_drbg_context *ctx,
void *out, size_t len);
/**
* \brief Inject additional seed bytes in AESCTR_DRBG.
*
* The provided seed bytes are added into the AESCTR_DRBG internal
* entropy pool. The process does not _replace_ existing entropy,
* thus pushing non-random bytes (i.e. bytes which are known to the
* attackers) does not degrade the overall quality of generated bytes.
*
* \param ctx AESCTR_DRBG context.
* \param seed additional seed.
* \param seed_len additional seed length (in bytes).
*/
void br_aesctr_drbg_update(br_aesctr_drbg_context *ctx,
const void *seed, size_t seed_len);
#ifdef __cplusplus
}
#endif
#endif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

487
enclave/include/cmockery.h Normal file
View File

@ -0,0 +1,487 @@
/*
* Copyright 2008 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef CMOCKERY_H_
#define CMOCKERY_H_
/*
* These headers or their equivalents should be included prior to including
* this header file.
*
* #include <stdarg.h>
* #include <stddef.h>
* #include <setjmp.h>
*
* This allows test applications to use custom definitions of C standard
* library functions and types.
*/
// For those who are used to __func__ from gcc.
#ifndef __func__
#define __func__ __FUNCTION__
#endif
/* Largest integral type. This type should be large enough to hold any
* pointer or integer supported by the compiler. */
#ifndef LargestIntegralType
#define LargestIntegralType uintmax_t
#endif // LargestIntegralType
// Printf format used to display LargestIntegralType.
#ifndef LargestIntegralTypePrintfFormat
#ifdef _WIN32
#define LargestIntegralTypePrintfFormat "%I64x"
#else
#define LargestIntegralTypePrintfFormat "%llx"
#endif // _WIN32
#endif // LargestIntegralTypePrintfFormat
// Perform an unsigned cast to LargestIntegralType.
#define cast_to_largest_integral_type(value) \
((LargestIntegralType)(value))
// Retrieves a return value for the current function.
#define mock() _mock(__func__, __FILE__, __LINE__)
/* Stores a value to be returned by the specified function later.
* The count parameter returns the number of times the value should be returned
* by mock(). If count is set to -1 the value will always be returned.
*/
#define will_return(function, value) \
_will_return(#function, __FILE__, __LINE__, \
cast_to_largest_integral_type(value), 1)
#define will_return_count(function, value, count) \
_will_return(#function, __FILE__, __LINE__, \
cast_to_largest_integral_type(value), count)
/* Add a custom parameter checking function. If the event parameter is NULL
* the event structure is allocated internally by this function. If event
* parameter is provided it must be allocated on the heap and doesn't need to
* be deallocated by the caller.
*/
#define expect_check(function, parameter, check_function, check_data) \
_expect_check(#function, #parameter, __FILE__, __LINE__, check_function, \
cast_to_largest_integral_type(check_data), NULL, 0)
/* Add an event to check a parameter, using check_expected(), against a set of
* values. See will_return() for a description of the count parameter.
*/
#define expect_in_set(function, parameter, value_array) \
expect_in_set_count(function, parameter, value_array, 1)
#define expect_in_set_count(function, parameter, value_array, count) \
_expect_in_set(#function, #parameter, __FILE__, __LINE__, value_array, \
sizeof(value_array) / sizeof((value_array)[0]), count)
#define expect_not_in_set(function, parameter, value_array) \
expect_not_in_set_count(function, parameter, value_array, 1)
#define expect_not_in_set_count(function, parameter, value_array, count) \
_expect_not_in_set( \
#function, #parameter, __FILE__, __LINE__, value_array, \
sizeof(value_array) / sizeof((value_array)[0]), count)
/* Add an event to check a parameter, using check_expected(), against a
* signed range. Where range is minimum <= value <= maximum.
* See will_return() for a description of the count parameter.
*/
#define expect_in_range(function, parameter, minimum, maximum) \
expect_in_range_count(function, parameter, minimum, maximum, 1)
#define expect_in_range_count(function, parameter, minimum, maximum, count) \
_expect_in_range(#function, #parameter, __FILE__, __LINE__, minimum, \
maximum, count)
/* Add an event to check a parameter, using check_expected(), against a
* signed range. Where range is value < minimum or value > maximum.
* See will_return() for a description of the count parameter.
*/
#define expect_not_in_range(function, parameter, minimum, maximum) \
expect_not_in_range_count(function, parameter, minimum, maximum, 1)
#define expect_not_in_range_count(function, parameter, minimum, maximum, \
count) \
_expect_not_in_range(#function, #parameter, __FILE__, __LINE__, \
minimum, maximum, count)
/* Add an event to check whether a parameter, using check_expected(), is or
* isn't a value. See will_return() for a description of the count parameter.
*/
#define expect_value(function, parameter, value) \
expect_value_count(function, parameter, value, 1)
#define expect_value_count(function, parameter, value, count) \
_expect_value(#function, #parameter, __FILE__, __LINE__, \
cast_to_largest_integral_type(value), count)
#define expect_not_value(function, parameter, value) \
expect_not_value_count(function, parameter, value, 1)
#define expect_not_value_count(function, parameter, value, count) \
_expect_not_value(#function, #parameter, __FILE__, __LINE__, \
cast_to_largest_integral_type(value), count)
/* Add an event to check whether a parameter, using check_expected(),
* is or isn't a string. See will_return() for a description of the count
* parameter.
*/
#define expect_string(function, parameter, string) \
expect_string_count(function, parameter, string, 1)
#define expect_string_count(function, parameter, string, count) \
_expect_string(#function, #parameter, __FILE__, __LINE__, \
(const char*)(string), count)
#define expect_not_string(function, parameter, string) \
expect_not_string_count(function, parameter, string, 1)
#define expect_not_string_count(function, parameter, string, count) \
_expect_not_string(#function, #parameter, __FILE__, __LINE__, \
(const char*)(string), count)
/* Add an event to check whether a parameter, using check_expected() does or
* doesn't match an area of memory. See will_return() for a description of
* the count parameter.
*/
#define expect_memory(function, parameter, memory, size) \
expect_memory_count(function, parameter, memory, size, 1)
#define expect_memory_count(function, parameter, memory, size, count) \
_expect_memory(#function, #parameter, __FILE__, __LINE__, \
(const void*)(memory), size, count)
#define expect_not_memory(function, parameter, memory, size) \
expect_not_memory_count(function, parameter, memory, size, 1)
#define expect_not_memory_count(function, parameter, memory, size, count) \
_expect_not_memory(#function, #parameter, __FILE__, __LINE__, \
(const void*)(memory), size, count)
/* Add an event to allow any value for a parameter checked using
* check_expected(). See will_return() for a description of the count
* parameter.
*/
#define expect_any(function, parameter) \
expect_any_count(function, parameter, 1)
#define expect_any_count(function, parameter, count) \
_expect_any(#function, #parameter, __FILE__, __LINE__, count)
/* Determine whether a function parameter is correct. This ensures the next
* value queued by one of the expect_*() macros matches the specified variable.
*/
#define check_expected(parameter) \
_check_expected(__func__, #parameter, __FILE__, __LINE__, \
cast_to_largest_integral_type(parameter))
// Assert that the given expression is true.
#define assert_true(c) _assert_true(cast_to_largest_integral_type(c), #c, \
__FILE__, __LINE__)
// Assert that the given expression is false.
#define assert_false(c) _assert_true(!(cast_to_largest_integral_type(c)), #c, \
__FILE__, __LINE__)
// Assert that the two given integers are equal, otherwise fail.
#define assert_int_equal(a, b) \
_assert_int_equal(cast_to_largest_integral_type(a), \
cast_to_largest_integral_type(b), \
__FILE__, __LINE__)
// Assert that the two given integers are not equal, otherwise fail.
#define assert_int_not_equal(a, b) \
_assert_int_not_equal(cast_to_largest_integral_type(a), \
cast_to_largest_integral_type(b), \
__FILE__, __LINE__)
// Assert that the two given strings are equal, otherwise fail.
#define assert_string_equal(a, b) \
_assert_string_equal((const char*)(a), (const char*)(b), __FILE__, \
__LINE__)
// Assert that the two given strings are not equal, otherwise fail.
#define assert_string_not_equal(a, b) \
_assert_string_not_equal((const char*)(a), (const char*)(b), __FILE__, \
__LINE__)
// Assert that the two given areas of memory are equal, otherwise fail.
#define assert_memory_equal(a, b, size) \
_assert_memory_equal((const char*)(a), (const char*)(b), size, __FILE__, \
__LINE__)
// Assert that the two given areas of memory are not equal, otherwise fail.
#define assert_memory_not_equal(a, b, size) \
_assert_memory_not_equal((const char*)(a), (const char*)(b), size, \
__FILE__, __LINE__)
// Assert that the specified value is >= minimum and <= maximum.
#define assert_in_range(value, minimum, maximum) \
_assert_in_range( \
cast_to_largest_integral_type(value), \
cast_to_largest_integral_type(minimum), \
cast_to_largest_integral_type(maximum), __FILE__, __LINE__)
// Assert that the specified value is < minumum or > maximum
#define assert_not_in_range(value, minimum, maximum) \
_assert_not_in_range( \
cast_to_largest_integral_type(value), \
cast_to_largest_integral_type(minimum), \
cast_to_largest_integral_type(maximum), __FILE__, __LINE__)
// Assert that the specified value is within a set.
#define assert_in_set(value, values, number_of_values) \
_assert_in_set(value, values, number_of_values, __FILE__, __LINE__)
// Assert that the specified value is not within a set.
#define assert_not_in_set(value, values, number_of_values) \
_assert_not_in_set(value, values, number_of_values, __FILE__, __LINE__)
// Forces the test to fail immediately and quit.
#define fail() _fail(__FILE__, __LINE__)
// Generic method to kick off testing
#define run_test(f) _run_test(#f, f, NULL, UNIT_TEST_FUNCTION_TYPE_TEST, NULL)
// Initializes a UnitTest structure.
#define unit_test(f) { #f, f, UNIT_TEST_FUNCTION_TYPE_TEST }
#define unit_test_setup(test, setup) \
{ #test "_" #setup, setup, UNIT_TEST_FUNCTION_TYPE_SETUP }
#define unit_test_teardown(test, teardown) \
{ #test "_" #teardown, teardown, UNIT_TEST_FUNCTION_TYPE_TEARDOWN }
/* Initialize an array of UnitTest structures with a setup function for a test
* and a teardown function. Either setup or teardown can be NULL.
*/
#define unit_test_setup_teardown(test, setup, teardown) \
unit_test_setup(test, setup), \
unit_test(test), \
unit_test_teardown(test, teardown)
/*
* Run tests specified by an array of UnitTest structures. The following
* example illustrates this macro's use with the unit_test macro.
*
* void Test0();
* void Test1();
*
* int main(int argc, char* argv[]) {
* const UnitTest tests[] = {
* unit_test(Test0);
* unit_test(Test1);
* };
* return run_tests(tests);
* }
*/
#define run_tests(tests) _run_tests(tests, sizeof(tests) / sizeof(tests)[0])
// Dynamic allocators
#define test_malloc(size) _test_malloc(size, __FILE__, __LINE__)
#define test_memalign(alignment, size) _test_memalign(alignment, size, __FILE__, __LINE__)
#define test_calloc(num, size) _test_calloc(num, size, __FILE__, __LINE__)
#define test_free(ptr) _test_free(ptr, __FILE__, __LINE__)
// Redirect malloc, calloc and free to the unit test allocators.
#if UNIT_TESTING
#define malloc test_malloc
#define memalign test_memalign
#define calloc test_calloc
#define free test_free
#endif // UNIT_TESTING
/*
* Ensure mock_assert() is called. If mock_assert() is called the assert
* expression string is returned.
* For example:
*
* #define assert mock_assert
*
* void showmessage(const char *message) {
* assert(message);
* }
*
* int main(int argc, const char* argv[]) {
* expect_assert_failure(show_message(NULL));
* printf("succeeded\n");
* return 0;
* }
*/
#define expect_assert_failure(function_call) \
{ \
const int expression = setjmp(global_expect_assert_env); \
global_expecting_assert = 1; \
if (expression) { \
print_message("Expected assertion %s occurred\n", \
*((const char**)&expression)); \
global_expecting_assert = 0; \
} else { \
function_call ; \
global_expecting_assert = 0; \
print_error("Expected assert in %s\n", #function_call); \
_fail(__FILE__, __LINE__); \
} \
}
// Function prototype for setup, test and teardown functions.
typedef void (*UnitTestFunction)(void **state);
// Function that determines whether a function parameter value is correct.
typedef int (*CheckParameterValue)(const LargestIntegralType value,
const LargestIntegralType check_value_data);
// Type of the unit test function.
typedef enum UnitTestFunctionType {
UNIT_TEST_FUNCTION_TYPE_TEST = 0,
UNIT_TEST_FUNCTION_TYPE_SETUP,
UNIT_TEST_FUNCTION_TYPE_TEARDOWN,
} UnitTestFunctionType;
/* Stores a unit test function with its name and type.
* NOTE: Every setup function must be paired with a teardown function. It's
* possible to specify NULL function pointers.
*/
typedef struct UnitTest {
const char* name;
UnitTestFunction function;
UnitTestFunctionType function_type;
} UnitTest;
// Location within some source code.
typedef struct SourceLocation {
const char* file;
int line;
} SourceLocation;
// Event that's called to check a parameter value.
typedef struct CheckParameterEvent {
SourceLocation location;
const char *parameter_name;
CheckParameterValue check_value;
LargestIntegralType check_value_data;
} CheckParameterEvent;
// Used by expect_assert_failure() and mock_assert().
extern int global_expecting_assert;
extern jmp_buf global_expect_assert_env;
// Retrieves a value for the given function, as set by "will_return".
LargestIntegralType _mock(const char * const function, const char* const file,
const int line);
void _expect_check(
const char* const function, const char* const parameter,
const char* const file, const int line,
const CheckParameterValue check_function,
const LargestIntegralType check_data, CheckParameterEvent * const event,
const int count);
void _expect_in_set(
const char* const function, const char* const parameter,
const char* const file, const int line, const LargestIntegralType values[],
const size_t number_of_values, const int count);
void _expect_not_in_set(
const char* const function, const char* const parameter,
const char* const file, const int line, const LargestIntegralType values[],
const size_t number_of_values, const int count);
void _expect_in_range(
const char* const function, const char* const parameter,
const char* const file, const int line,
const LargestIntegralType minimum,
const LargestIntegralType maximum, const int count);
void _expect_not_in_range(
const char* const function, const char* const parameter,
const char* const file, const int line,
const LargestIntegralType minimum,
const LargestIntegralType maximum, const int count);
void _expect_value(
const char* const function, const char* const parameter,
const char* const file, const int line, const LargestIntegralType value,
const int count);
void _expect_not_value(
const char* const function, const char* const parameter,
const char* const file, const int line, const LargestIntegralType value,
const int count);
void _expect_string(
const char* const function, const char* const parameter,
const char* const file, const int line, const char* string,
const int count);
void _expect_not_string(
const char* const function, const char* const parameter,
const char* const file, const int line, const char* string,
const int count);
void _expect_memory(
const char* const function, const char* const parameter,
const char* const file, const int line, const void* const memory,
const size_t size, const int count);
void _expect_not_memory(
const char* const function, const char* const parameter,
const char* const file, const int line, const void* const memory,
const size_t size, const int count);
void _expect_any(
const char* const function, const char* const parameter,
const char* const file, const int line, const int count);
void _check_expected(
const char * const function_name, const char * const parameter_name,
const char* file, const int line, const LargestIntegralType value);
// Can be used to replace assert in tested code so that in conjuction with
// check_assert() it's possible to determine whether an assert condition has
// failed without stopping a test.
void mock_assert(const int result, const char* const expression,
const char * const file, const int line);
void _will_return(const char * const function_name, const char * const file,
const int line, const LargestIntegralType value,
const int count);
void _assert_true(const LargestIntegralType result,
const char* const expression,
const char * const file, const int line);
void _assert_int_equal(
const LargestIntegralType a, const LargestIntegralType b,
const char * const file, const int line);
void _assert_int_not_equal(
const LargestIntegralType a, const LargestIntegralType b,
const char * const file, const int line);
void _assert_string_equal(const char * const a, const char * const b,
const char * const file, const int line);
void _assert_string_not_equal(const char * const a, const char * const b,
const char *file, const int line);
void _assert_memory_equal(const void * const a, const void * const b,
const size_t size, const char* const file,
const int line);
void _assert_memory_not_equal(const void * const a, const void * const b,
const size_t size, const char* const file,
const int line);
void _assert_in_range(
const LargestIntegralType value, const LargestIntegralType minimum,
const LargestIntegralType maximum, const char* const file, const int line);
void _assert_not_in_range(
const LargestIntegralType value, const LargestIntegralType minimum,
const LargestIntegralType maximum, const char* const file, const int line);
void _assert_in_set(
const LargestIntegralType value, const LargestIntegralType values[],
const size_t number_of_values, const char* const file, const int line);
void _assert_not_in_set(
const LargestIntegralType value, const LargestIntegralType values[],
const size_t number_of_values, const char* const file, const int line);
void* _test_malloc(const size_t size, const char* file, const int line);
void* _test_memalign(const size_t alignment, const size_t size, const char* file, const int line);
void* _test_calloc(const size_t number_of_elements, const size_t size,
const char* file, const int line);
void _test_free(void* const ptr, const char* file, const int line);
void _fail(const char * const file, const int line);
int _run_test(
const char * const function_name, const UnitTestFunction Function,
void ** const state, const UnitTestFunctionType function_type,
const void* const heap_check_point);
int _run_tests(const UnitTest * const tests, const size_t number_of_tests);
// Standard output and error print methods.
void print_message(const char* const format, ...);
void print_error(const char* const format, ...);
void vprint_message(const char* const format, va_list args);
void vprint_error(const char* const format, va_list args);
#endif // CMOCKERY_H_

View File

@ -0,0 +1,7 @@
#ifndef _CURVE25519_DONNA_H
#define _CURVE25519_DONNA_H
int curve25519_donna(unsigned char *, const unsigned char *, const unsigned char *);
#endif

View File

@ -0,0 +1,37 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _DLMALLOC_H
#define _DLMALLOC_H
#include <stdlib.h>
struct mallinfo dlmallinfo();
struct mallinfo {
int arena; /* non-mmapped space allocated from system */
int ordblks; /* number of free chunks */
int smblks; /* always 0 */
int hblks; /* always 0 */
int hblkhd; /* space in mmapped regions */
int usmblks; /* maximum total allocated space */
int fsmblks; /* always 0 */
int uordblks; /* total allocated space */
int fordblks; /* total free space */
int keepcost; /* releasable (via malloc_trim) space */
};
#endif

473
enclave/include/kbupd.proto Normal file
View File

@ -0,0 +1,473 @@
syntax = "proto2";
package protobufs.kbupd;
import "kbupd_client.proto";
//
// shared types
//
message ServiceId {
required bytes id = 1;
}
message BackupId {
required bytes id = 1;
}
message PartitionKeyRangePB {
required BackupId first = 1;
required BackupId last = 2;
}
message IasReport {
required bytes body = 2;
required bytes signature = 3;
repeated bytes certificates = 4;
}
message AttestationParameters {
required uint64 unix_timestamp_seconds = 1;
}
enum XferControlCommand {
START = 1;
FINISH = 2;
CANCEL = 3;
PAUSE = 4;
RESUME = 5;
}
//
// transaction requests
//
message CreateBackupRequest {
required BackupId backup_id = 1;
}
message CreateBackupReply {
required bytes nonce = 2;
optional uint32 tries = 3;
}
message DeleteBackupRequest {
required BackupId backup_id = 1;
}
message DeleteBackupReply {
}
//
// untrusted messages
//
message UntrustedMessageBatch {
repeated UntrustedMessage messages = 1;
}
message UntrustedMessage {
oneof inner {
StartFrontendRequest start_frontend_request = 1;
StartReplicaRequest start_replica_request = 2;
StartReplicaGroupRequest start_replica_group_request = 3;
UntrustedTransactionRequest untrusted_transaction_request = 4;
UntrustedXferRequest untrusted_xfer_request = 5;
GetEnclaveStatusRequest get_enclave_status_request = 6;
GetQeInfoReply get_qe_info_reply = 8;
GetQuoteReply get_quote_reply = 9;
GetAttestationReply get_attestation_reply = 10;
NewMessageSignal new_message_signal = 11;
TimerTickSignal timer_tick_signal = 12;
SetFrontendConfigSignal set_frontend_config_signal = 13;
SetReplicaConfigSignal set_replica_config_signal = 14;
ResetPeerSignal reset_peer_signal = 15;
SetVerboseLoggingSignal set_verbose_logging_signal = 16;
}
reserved 7;
}
message PartitionConfig {
required bytes group_id = 1;
optional PartitionKeyRangePB range = 2;
repeated bytes node_ids = 3;
}
message StartFrontendRequest {
repeated PartitionConfig partitions = 1;
required EnclaveFrontendConfig config = 2;
}
message EnclaveFrontendConfig {
required uint32 replica_timeout_ticks = 1;
required uint32 request_quote_ticks = 2;
required uint32 min_connect_timeout_ticks = 3;
required uint32 max_connect_timeout_ticks = 4;
required uint32 pending_request_count = 5;
required uint32 pending_request_ttl = 6;
}
message SourcePartitionConfig {
required PartitionKeyRangePB range = 1;
repeated bytes node_ids = 2;
}
message StartReplicaRequest {
required EnclaveReplicaConfig config = 1;
}
message EnclaveReplicaConfig {
required uint32 election_timeout_ticks = 1;
required uint32 heartbeat_timeout_ticks = 2;
required uint32 request_quote_ticks = 3;
required uint32 min_connect_timeout_ticks = 4;
required uint32 max_connect_timeout_ticks = 5;
required uint32 attestation_expiry_ticks = 6;
required uint32 replication_chunk_size = 7;
required uint32 transfer_chunk_size = 8;
required uint32 storage_page_cache_size = 10;
required uint32 raft_log_index_page_cache_size = 13;
required uint32 max_frontend_count = 14;
}
message StartReplicaGroupRequest {
repeated bytes peer_node_ids = 1;
required EnclaveReplicaGroupConfig config = 2;
optional SourcePartitionConfig source_partition = 3;
}
message EnclaveReplicaGroupConfig {
required uint32 storage_size = 1;
required uint64 raft_log_data_size = 2;
required uint32 raft_log_index_size = 3;
}
message NewMessageSignal {
required bytes node_id = 1;
required bytes data = 2;
required bool syn = 3;
}
message TimerTickSignal {
required fixed64 now_secs = 1;
}
message SetFrontendConfigSignal {
required EnclaveFrontendConfig config = 1;
}
message SetReplicaConfigSignal {
required EnclaveReplicaConfig config = 1;
}
message ResetPeerSignal {
required bytes peer_node_id = 1;
}
message SetVerboseLoggingSignal {
required bool verbose_logging = 1;
}
message GetQeInfoReply {
required bytes mrenclave = 1;
required uint64 flags = 2;
required uint64 xfrm = 3;
required uint32 misc_select = 4;
required uint32 config_svn = 5;
required bytes config_id = 6;
}
message GetQuoteReply {
required bytes request_id = 1;
required bytes sgx_quote = 2;
}
message GetAttestationReply {
required bytes request_id = 1;
required IasReport ias_report = 2;
}
message UntrustedTransactionRequest {
required uint64 request_id = 1;
oneof data {
CreateBackupRequest create_backup_request = 2;
DeleteBackupRequest delete_backup_request = 3;
};
}
message UntrustedXferRequest {
required uint64 request_id = 1;
oneof data {
XferControlCommand xfer_control_command = 2;
};
}
message GetEnclaveStatusRequest {
required bool memory_status = 1;
}
//
// enclave messages
//
message EnclaveMessageBatch {
repeated EnclaveMessage messages = 1;
}
message EnclaveMessage {
oneof inner {
StartFrontendReply start_frontend_reply = 1;
StartReplicaReply start_replica_reply = 2;
StartReplicaGroupReply start_replica_group_reply = 3;
UntrustedTransactionReply untrusted_transaction_reply = 4;
UntrustedXferReply untrusted_xfer_reply = 5;
GetEnclaveStatusReply get_enclave_status_reply = 6;
SendMessageRequest send_message_request = 7;
GetQeInfoRequest get_qe_info_request = 8;
GetQuoteRequest get_quote_request = 9;
GetAttestationRequest get_attestation_request = 10;
EnclaveLogSignal enclave_log_signal = 11;
EnclaveTransactionSignal enclave_transaction_signal = 12;
}
}
message StartFrontendReply {
required bytes node_id = 1;
}
message StartReplicaReply {
required bytes node_id = 1;
}
message StartReplicaGroupReply {
optional ServiceId service_id = 1;
optional bytes group_id = 2;
}
message GetEnclaveStatusReply {
oneof inner {
EnclaveReplicaStatus replica_status = 1;
EnclaveFrontendStatus frontend_status = 2;
}
}
message SendMessageRequest {
required bytes node_id = 1;
required bytes data = 2;
required bool syn = 3;
optional bytes debug_msg = 4;
}
message GetQeInfoRequest {
}
message GetQuoteRequest {
required bytes request_id = 1;
required bytes sgx_report = 2;
}
message GetAttestationRequest {
required bytes request_id = 1;
required bytes sgx_quote = 2;
}
message UntrustedTransactionReply {
required uint64 request_id = 1;
oneof data {
CreateBackupReply create_backup_reply = 2;
DeleteBackupReply delete_backup_reply = 3;
};
}
enum UntrustedXferReplyStatus {
UNKNOWN = 0;
OK = 1;
NOT_LEADER = 2;
INVALID_STATE = 3;
}
message UntrustedXferReply {
required uint64 request_id = 1;
required UntrustedXferReplyStatus status = 2;
}
enum EnclaveLogLevel {
ERROR = 0;
WARN = 1;
INFO = 2;
DEBUG = 3;
}
message EnclaveLogSignal {
required bytes message = 1;
required bytes module = 2;
required bytes file = 3;
required uint32 line = 4;
required EnclaveLogLevel level = 5;
}
message EnclaveTransactionSignal {
required uint64 log_index = 1;
oneof transaction {
EnclaveFrontendRequestTransaction frontend_request = 2;
EnclaveStartXferTransaction start_xfer = 3;
EnclaveSetSidTransaction set_sid = 4;
EnclaveRemoveChunkTransaction remove_chunk = 5;
EnclaveApplyChunkTransaction apply_chunk = 6;
EnclavePauseXferTransaction pause_xfer = 7;
EnclaveResumeXferTransaction resume_xfer = 8;
EnclaveFinishXferTransaction finish_xfer = 9;
EnclaveSetTimeTransaction set_time = 10;
};
}
//
// enclave transactions
//
message EnclaveFrontendRequestTransaction {
oneof transaction {
EnclaveCreateBackupTransaction create = 1;
EnclaveBackupTransaction backup = 2;
EnclaveRestoreTransaction restore = 3;
EnclaveDeleteBackupTransaction delete = 4;
EnclaveTransactionErrorXferInProgress xfer_in_progress = 5;
EnclaveTransactionErrorWrongPartition wrong_partition = 6;
EnclaveTransactionErrorInvalidRequest invalid_request = 7;
EnclaveTransactionErrorInternalError internal_error = 8;
}
}
message EnclaveCreateBackupTransaction {
required BackupId backup_id = 1;
}
message EnclaveBackupTransaction {
required BackupId backup_id = 1;
required kbupd_client.BackupResponse.Status status = 2;
}
message EnclaveRestoreTransaction {
required BackupId backup_id = 1;
required kbupd_client.RestoreResponse.Status status = 2;
}
message EnclaveDeleteBackupTransaction {
required BackupId backup_id = 1;
}
message EnclaveTransactionErrorXferInProgress {
}
message EnclaveTransactionErrorWrongPartition {
required bool new_partition_unknown = 1;
}
message EnclaveTransactionErrorInvalidRequest {
}
message EnclaveTransactionErrorInternalError {
}
message EnclaveStartXferTransaction {
}
message EnclaveSetSidTransaction {
optional ServiceId service_id = 1;
}
message EnclaveRemoveChunkTransaction {
optional PartitionKeyRangePB chunk_range = 1;
}
message EnclaveApplyChunkTransaction {
optional PartitionKeyRangePB chunk_range = 1;
repeated BackupId chunk_ids = 2;
}
message EnclavePauseXferTransaction {
}
message EnclaveResumeXferTransaction {
optional PartitionKeyRangePB chunk_range = 1;
}
message EnclaveFinishXferTransaction {
}
message EnclaveSetTimeTransaction {
optional uint64 now_secs = 1;
}
//
// enclave status
//
message EnclaveMemoryStatus {
required uint32 footprint_bytes = 1;
required uint32 used_bytes = 2;
required uint32 free_chunks = 3;
}
message EnclaveReplicaStatus {
optional EnclaveMemoryStatus memory_status = 1;
optional EnclaveReplicaPartitionStatus partition = 2;
}
message EnclaveReplicaPartitionStatus {
required bytes group_id = 1;
optional bytes service_id = 2;
optional PartitionKeyRangePB range = 3;
repeated EnclavePeerStatus peers = 4;
required AttestationParameters min_attestation = 5;
required bool is_leader = 6;
required uint64 current_term = 7;
required uint64 prev_log_index = 8;
required uint64 last_applied_index = 9;
required uint64 commit_index = 10;
required uint64 last_log_index = 11;
required uint64 last_log_term = 12;
required uint64 log_data_length = 13;
required uint64 backup_count = 14;
oneof xfer_status {
EnclaveIncomingXferStatus incoming_xfer_status = 15;
EnclaveOutgoingXferStatus outgoing_xfer_status = 16;
}
}
message EnclavePeerStatus {
required bytes node_id = 1;
optional AttestationParameters attestation = 2;
optional EnclavePeerReplicationStatus replication_status = 3;
required bool is_leader = 4;
required uint64 inflight_requests = 5;
required uint64 unsent_requests = 6;
}
message EnclavePeerReplicationStatus {
required uint64 next_index = 1;
required uint64 match_index = 2;
optional uint64 inflight_index = 3;
required bool probing = 4;
}
message EnclaveIncomingXferStatus {
required PartitionKeyRangePB desired_range = 1;
repeated EnclavePeerStatus nodes = 2;
}
message EnclaveOutgoingXferStatus {
required bytes group_id = 1;
required PartitionKeyRangePB full_xfer_range = 2;
optional PartitionKeyRangePB current_chunk_range = 3;
required bool paused = 4;
optional AttestationParameters min_attestation = 5;
repeated EnclavePeerStatus nodes = 6;
}
message EnclaveFrontendStatus {
optional EnclaveMemoryStatus memory_status = 1;
repeated EnclaveFrontendPartitionStatus partitions = 2;
repeated EnclaveFrontendRangeStatus ranges = 3;
}
message EnclaveFrontendPartitionStatus {
required bytes group_id = 1;
repeated EnclavePeerStatus nodes = 2;
}
message EnclaveFrontendRangeStatus {
required PartitionKeyRangePB range = 1;
required bytes group_id = 2;
}

View File

@ -0,0 +1,79 @@
syntax = "proto2";
package protobufs.kbupd_client;
message Request {
optional BackupRequest backup = 1;
optional RestoreRequest restore = 2;
optional DeleteRequest delete = 3;
}
message Response {
optional BackupResponse backup = 1;
optional RestoreResponse restore = 2;
optional DeleteResponse delete = 3;
}
//
// backup
//
message BackupRequest {
optional bytes service_id = 1;
optional bytes backup_id = 2;
optional bytes nonce = 3;
optional uint64 valid_from = 4;
optional bytes data = 5;
optional bytes pin = 6;
optional uint32 tries = 7;
}
message BackupResponse {
enum Status {
OK = 1;
ALREADY_EXISTS = 2;
NOT_YET_VALID = 3;
}
optional Status status = 1;
optional bytes nonce = 2;
}
//
// restore
//
message RestoreRequest {
optional bytes service_id = 1;
optional bytes backup_id = 2;
optional bytes nonce = 3;
optional uint64 valid_from = 4;
optional bytes pin = 5;
}
message RestoreResponse {
enum Status {
OK = 1;
NONCE_MISMATCH = 2;
NOT_YET_VALID = 3;
MISSING = 4;
PIN_MISMATCH = 5;
}
optional Status status = 1;
optional bytes nonce = 2;
optional bytes data = 3;
optional uint32 tries = 4;
}
//
// delete
//
message DeleteRequest {
optional bytes service_id = 1;
optional bytes backup_id = 2;
}
message DeleteResponse {
}

View File

@ -0,0 +1,16 @@
enclave {
include "kbupd_sgxsd_callbacks.h"
from "sgxsd.edl" import *;
from "sgx_tstdc.edl" import *;
trusted {
public void kbupd_enclave_recv_untrusted_msg
([in, size=data_size] const uint8_t *data, size_t data_size);
};
untrusted {
void kbupd_enclave_ocall_recv_enclave_msg
([in, size=data_size] const uint8_t *data, size_t data_size);
void *kbupd_enclave_ocall_alloc([in, out] size_t *size);
void kbupd_enclave_ocall_panic([in, size=msg_size] const uint8_t *msg, size_t msg_size);
};
};

View File

@ -0,0 +1,949 @@
#include "kbupd_enclave_t.h"
#include "sgx_trts.h" /* for sgx_ocalloc, sgx_is_outside_enclave */
#include "sgx_lfence.h" /* for sgx_lfence */
#include <errno.h>
#include <mbusafecrt.h> /* for memcpy_s etc */
#include <stdlib.h> /* for malloc/free etc */
#define CHECK_REF_POINTER(ptr, siz) do { \
if (!(ptr) || ! sgx_is_outside_enclave((ptr), (siz))) \
return SGX_ERROR_INVALID_PARAMETER;\
} while (0)
#define CHECK_UNIQUE_POINTER(ptr, siz) do { \
if ((ptr) && ! sgx_is_outside_enclave((ptr), (siz))) \
return SGX_ERROR_INVALID_PARAMETER;\
} while (0)
#define CHECK_ENCLAVE_POINTER(ptr, siz) do { \
if ((ptr) && ! sgx_is_within_enclave((ptr), (siz))) \
return SGX_ERROR_INVALID_PARAMETER;\
} while (0)
#define ADD_ASSIGN_OVERFLOW(a, b) ( \
((a) += (b)) < (b) \
)
typedef struct ms_kbupd_enclave_recv_untrusted_msg_t {
const uint8_t* ms_data;
size_t ms_data_size;
} ms_kbupd_enclave_recv_untrusted_msg_t;
typedef struct ms_sgxsd_enclave_node_init_t {
sgx_status_t ms_retval;
const sgxsd_node_init_args_t* ms_p_args;
} ms_sgxsd_enclave_node_init_t;
typedef struct ms_sgxsd_enclave_get_next_report_t {
sgx_status_t ms_retval;
sgx_target_info_t ms_qe_target_info;
sgx_report_t* ms_p_report;
} ms_sgxsd_enclave_get_next_report_t;
typedef struct ms_sgxsd_enclave_set_current_quote_t {
sgx_status_t ms_retval;
} ms_sgxsd_enclave_set_current_quote_t;
typedef struct ms_sgxsd_enclave_negotiate_request_t {
sgx_status_t ms_retval;
const sgxsd_request_negotiation_request_t* ms_p_request;
sgxsd_request_negotiation_response_t* ms_p_response;
} ms_sgxsd_enclave_negotiate_request_t;
typedef struct ms_sgxsd_enclave_server_start_t {
sgx_status_t ms_retval;
const sgxsd_server_init_args_t* ms_p_args;
sgxsd_server_state_handle_t ms_state_handle;
} ms_sgxsd_enclave_server_start_t;
typedef struct ms_sgxsd_enclave_server_call_t {
sgx_status_t ms_retval;
const sgxsd_server_handle_call_args_t* ms_p_args;
const sgxsd_msg_header_t* ms_msg_header;
uint8_t* ms_msg_data;
size_t ms_msg_size;
sgxsd_msg_tag_t ms_msg_tag;
sgxsd_server_state_handle_t ms_state_handle;
} ms_sgxsd_enclave_server_call_t;
typedef struct ms_sgxsd_enclave_server_stop_t {
sgx_status_t ms_retval;
const sgxsd_server_terminate_args_t* ms_p_args;
sgxsd_server_state_handle_t ms_state_handle;
} ms_sgxsd_enclave_server_stop_t;
typedef struct ms_kbupd_enclave_ocall_recv_enclave_msg_t {
const uint8_t* ms_data;
size_t ms_data_size;
} ms_kbupd_enclave_ocall_recv_enclave_msg_t;
typedef struct ms_kbupd_enclave_ocall_alloc_t {
void* ms_retval;
size_t* ms_size;
} ms_kbupd_enclave_ocall_alloc_t;
typedef struct ms_kbupd_enclave_ocall_panic_t {
const uint8_t* ms_msg;
size_t ms_msg_size;
} ms_kbupd_enclave_ocall_panic_t;
typedef struct ms_sgxsd_ocall_reply_t {
sgx_status_t ms_retval;
const sgxsd_msg_header_t* ms_reply_header;
const uint8_t* ms_reply_data;
size_t ms_reply_data_size;
sgxsd_msg_tag_t ms_msg_tag;
} ms_sgxsd_ocall_reply_t;
typedef struct ms_sgx_oc_cpuidex_t {
int* ms_cpuinfo;
int ms_leaf;
int ms_subleaf;
} ms_sgx_oc_cpuidex_t;
typedef struct ms_sgx_thread_wait_untrusted_event_ocall_t {
int ms_retval;
const void* ms_self;
} ms_sgx_thread_wait_untrusted_event_ocall_t;
typedef struct ms_sgx_thread_set_untrusted_event_ocall_t {
int ms_retval;
const void* ms_waiter;
} ms_sgx_thread_set_untrusted_event_ocall_t;
typedef struct ms_sgx_thread_setwait_untrusted_events_ocall_t {
int ms_retval;
const void* ms_waiter;
const void* ms_self;
} ms_sgx_thread_setwait_untrusted_events_ocall_t;
typedef struct ms_sgx_thread_set_multiple_untrusted_events_ocall_t {
int ms_retval;
const void** ms_waiters;
size_t ms_total;
} ms_sgx_thread_set_multiple_untrusted_events_ocall_t;
static sgx_status_t SGX_CDECL sgx_kbupd_enclave_recv_untrusted_msg(void* pms)
{
CHECK_REF_POINTER(pms, sizeof(ms_kbupd_enclave_recv_untrusted_msg_t));
//
// fence after pointer checks
//
sgx_lfence();
ms_kbupd_enclave_recv_untrusted_msg_t* ms = SGX_CAST(ms_kbupd_enclave_recv_untrusted_msg_t*, pms);
sgx_status_t status = SGX_SUCCESS;
const uint8_t* _tmp_data = ms->ms_data;
size_t _tmp_data_size = ms->ms_data_size;
size_t _len_data = _tmp_data_size;
uint8_t* _in_data = NULL;
CHECK_UNIQUE_POINTER(_tmp_data, _len_data);
//
// fence after pointer checks
//
sgx_lfence();
if (_tmp_data != NULL && _len_data != 0) {
if ( _len_data % sizeof(*_tmp_data) != 0)
{
status = SGX_ERROR_INVALID_PARAMETER;
goto err;
}
_in_data = (uint8_t*)malloc(_len_data);
if (_in_data == NULL) {
status = SGX_ERROR_OUT_OF_MEMORY;
goto err;
}
if (memcpy_s(_in_data, _len_data, _tmp_data, _len_data)) {
status = SGX_ERROR_UNEXPECTED;
goto err;
}
}
kbupd_enclave_recv_untrusted_msg((const uint8_t*)_in_data, _tmp_data_size);
err:
if (_in_data) free(_in_data);
return status;
}
static sgx_status_t SGX_CDECL sgx_sgxsd_enclave_node_init(void* pms)
{
CHECK_REF_POINTER(pms, sizeof(ms_sgxsd_enclave_node_init_t));
//
// fence after pointer checks
//
sgx_lfence();
ms_sgxsd_enclave_node_init_t* ms = SGX_CAST(ms_sgxsd_enclave_node_init_t*, pms);
sgx_status_t status = SGX_SUCCESS;
const sgxsd_node_init_args_t* _tmp_p_args = ms->ms_p_args;
size_t _len_p_args = sizeof(sgxsd_node_init_args_t);
sgxsd_node_init_args_t* _in_p_args = NULL;
CHECK_UNIQUE_POINTER(_tmp_p_args, _len_p_args);
//
// fence after pointer checks
//
sgx_lfence();
if (_tmp_p_args != NULL && _len_p_args != 0) {
_in_p_args = (sgxsd_node_init_args_t*)malloc(_len_p_args);
if (_in_p_args == NULL) {
status = SGX_ERROR_OUT_OF_MEMORY;
goto err;
}
if (memcpy_s(_in_p_args, _len_p_args, _tmp_p_args, _len_p_args)) {
status = SGX_ERROR_UNEXPECTED;
goto err;
}
}
ms->ms_retval = sgxsd_enclave_node_init((const sgxsd_node_init_args_t*)_in_p_args);
err:
if (_in_p_args) free(_in_p_args);
return status;
}
static sgx_status_t SGX_CDECL sgx_sgxsd_enclave_get_next_report(void* pms)
{
CHECK_REF_POINTER(pms, sizeof(ms_sgxsd_enclave_get_next_report_t));
//
// fence after pointer checks
//
sgx_lfence();
ms_sgxsd_enclave_get_next_report_t* ms = SGX_CAST(ms_sgxsd_enclave_get_next_report_t*, pms);
sgx_status_t status = SGX_SUCCESS;
sgx_report_t* _tmp_p_report = ms->ms_p_report;
size_t _len_p_report = sizeof(sgx_report_t);
sgx_report_t* _in_p_report = NULL;
CHECK_UNIQUE_POINTER(_tmp_p_report, _len_p_report);
//
// fence after pointer checks
//
sgx_lfence();
if (_tmp_p_report != NULL && _len_p_report != 0) {
if ((_in_p_report = (sgx_report_t*)malloc(_len_p_report)) == NULL) {
status = SGX_ERROR_OUT_OF_MEMORY;
goto err;
}
memset((void*)_in_p_report, 0, _len_p_report);
}
ms->ms_retval = sgxsd_enclave_get_next_report(ms->ms_qe_target_info, _in_p_report);
if (_in_p_report) {
if (memcpy_s(_tmp_p_report, _len_p_report, _in_p_report, _len_p_report)) {
status = SGX_ERROR_UNEXPECTED;
goto err;
}
}
err:
if (_in_p_report) free(_in_p_report);
return status;
}
static sgx_status_t SGX_CDECL sgx_sgxsd_enclave_set_current_quote(void* pms)
{
CHECK_REF_POINTER(pms, sizeof(ms_sgxsd_enclave_set_current_quote_t));
//
// fence after pointer checks
//
sgx_lfence();
ms_sgxsd_enclave_set_current_quote_t* ms = SGX_CAST(ms_sgxsd_enclave_set_current_quote_t*, pms);
sgx_status_t status = SGX_SUCCESS;
ms->ms_retval = sgxsd_enclave_set_current_quote();
return status;
}
static sgx_status_t SGX_CDECL sgx_sgxsd_enclave_negotiate_request(void* pms)
{
CHECK_REF_POINTER(pms, sizeof(ms_sgxsd_enclave_negotiate_request_t));
//
// fence after pointer checks
//
sgx_lfence();
ms_sgxsd_enclave_negotiate_request_t* ms = SGX_CAST(ms_sgxsd_enclave_negotiate_request_t*, pms);
sgx_status_t status = SGX_SUCCESS;
const sgxsd_request_negotiation_request_t* _tmp_p_request = ms->ms_p_request;
size_t _len_p_request = sizeof(sgxsd_request_negotiation_request_t);
sgxsd_request_negotiation_request_t* _in_p_request = NULL;
sgxsd_request_negotiation_response_t* _tmp_p_response = ms->ms_p_response;
size_t _len_p_response = sizeof(sgxsd_request_negotiation_response_t);
sgxsd_request_negotiation_response_t* _in_p_response = NULL;
CHECK_UNIQUE_POINTER(_tmp_p_request, _len_p_request);
CHECK_UNIQUE_POINTER(_tmp_p_response, _len_p_response);
//
// fence after pointer checks
//
sgx_lfence();
if (_tmp_p_request != NULL && _len_p_request != 0) {
_in_p_request = (sgxsd_request_negotiation_request_t*)malloc(_len_p_request);
if (_in_p_request == NULL) {
status = SGX_ERROR_OUT_OF_MEMORY;
goto err;
}
if (memcpy_s(_in_p_request, _len_p_request, _tmp_p_request, _len_p_request)) {
status = SGX_ERROR_UNEXPECTED;
goto err;
}
}
if (_tmp_p_response != NULL && _len_p_response != 0) {
if ((_in_p_response = (sgxsd_request_negotiation_response_t*)malloc(_len_p_response)) == NULL) {
status = SGX_ERROR_OUT_OF_MEMORY;
goto err;
}
memset((void*)_in_p_response, 0, _len_p_response);
}
ms->ms_retval = sgxsd_enclave_negotiate_request((const sgxsd_request_negotiation_request_t*)_in_p_request, _in_p_response);
if (_in_p_response) {
if (memcpy_s(_tmp_p_response, _len_p_response, _in_p_response, _len_p_response)) {
status = SGX_ERROR_UNEXPECTED;
goto err;
}
}
err:
if (_in_p_request) free(_in_p_request);
if (_in_p_response) free(_in_p_response);
return status;
}
static sgx_status_t SGX_CDECL sgx_sgxsd_enclave_server_start(void* pms)
{
CHECK_REF_POINTER(pms, sizeof(ms_sgxsd_enclave_server_start_t));
//
// fence after pointer checks
//
sgx_lfence();
ms_sgxsd_enclave_server_start_t* ms = SGX_CAST(ms_sgxsd_enclave_server_start_t*, pms);
sgx_status_t status = SGX_SUCCESS;
const sgxsd_server_init_args_t* _tmp_p_args = ms->ms_p_args;
size_t _len_p_args = sizeof(sgxsd_server_init_args_t);
sgxsd_server_init_args_t* _in_p_args = NULL;
CHECK_UNIQUE_POINTER(_tmp_p_args, _len_p_args);
//
// fence after pointer checks
//
sgx_lfence();
if (_tmp_p_args != NULL && _len_p_args != 0) {
_in_p_args = (sgxsd_server_init_args_t*)malloc(_len_p_args);
if (_in_p_args == NULL) {
status = SGX_ERROR_OUT_OF_MEMORY;
goto err;
}
if (memcpy_s(_in_p_args, _len_p_args, _tmp_p_args, _len_p_args)) {
status = SGX_ERROR_UNEXPECTED;
goto err;
}
}
ms->ms_retval = sgxsd_enclave_server_start((const sgxsd_server_init_args_t*)_in_p_args, ms->ms_state_handle);
err:
if (_in_p_args) free(_in_p_args);
return status;
}
static sgx_status_t SGX_CDECL sgx_sgxsd_enclave_server_call(void* pms)
{
CHECK_REF_POINTER(pms, sizeof(ms_sgxsd_enclave_server_call_t));
//
// fence after pointer checks
//
sgx_lfence();
ms_sgxsd_enclave_server_call_t* ms = SGX_CAST(ms_sgxsd_enclave_server_call_t*, pms);
sgx_status_t status = SGX_SUCCESS;
const sgxsd_server_handle_call_args_t* _tmp_p_args = ms->ms_p_args;
size_t _len_p_args = sizeof(sgxsd_server_handle_call_args_t);
sgxsd_server_handle_call_args_t* _in_p_args = NULL;
const sgxsd_msg_header_t* _tmp_msg_header = ms->ms_msg_header;
size_t _len_msg_header = sizeof(sgxsd_msg_header_t);
sgxsd_msg_header_t* _in_msg_header = NULL;
uint8_t* _tmp_msg_data = ms->ms_msg_data;
size_t _tmp_msg_size = ms->ms_msg_size;
size_t _len_msg_data = _tmp_msg_size;
uint8_t* _in_msg_data = NULL;
CHECK_UNIQUE_POINTER(_tmp_p_args, _len_p_args);
CHECK_UNIQUE_POINTER(_tmp_msg_header, _len_msg_header);
CHECK_UNIQUE_POINTER(_tmp_msg_data, _len_msg_data);
//
// fence after pointer checks
//
sgx_lfence();
if (_tmp_p_args != NULL && _len_p_args != 0) {
_in_p_args = (sgxsd_server_handle_call_args_t*)malloc(_len_p_args);
if (_in_p_args == NULL) {
status = SGX_ERROR_OUT_OF_MEMORY;
goto err;
}
if (memcpy_s(_in_p_args, _len_p_args, _tmp_p_args, _len_p_args)) {
status = SGX_ERROR_UNEXPECTED;
goto err;
}
}
if (_tmp_msg_header != NULL && _len_msg_header != 0) {
_in_msg_header = (sgxsd_msg_header_t*)malloc(_len_msg_header);
if (_in_msg_header == NULL) {
status = SGX_ERROR_OUT_OF_MEMORY;
goto err;
}
if (memcpy_s(_in_msg_header, _len_msg_header, _tmp_msg_header, _len_msg_header)) {
status = SGX_ERROR_UNEXPECTED;
goto err;
}
}
if (_tmp_msg_data != NULL && _len_msg_data != 0) {
if ( _len_msg_data % sizeof(*_tmp_msg_data) != 0)
{
status = SGX_ERROR_INVALID_PARAMETER;
goto err;
}
_in_msg_data = (uint8_t*)malloc(_len_msg_data);
if (_in_msg_data == NULL) {
status = SGX_ERROR_OUT_OF_MEMORY;
goto err;
}
if (memcpy_s(_in_msg_data, _len_msg_data, _tmp_msg_data, _len_msg_data)) {
status = SGX_ERROR_UNEXPECTED;
goto err;
}
}
ms->ms_retval = sgxsd_enclave_server_call((const sgxsd_server_handle_call_args_t*)_in_p_args, (const sgxsd_msg_header_t*)_in_msg_header, _in_msg_data, _tmp_msg_size, ms->ms_msg_tag, ms->ms_state_handle);
err:
if (_in_p_args) free(_in_p_args);
if (_in_msg_header) free(_in_msg_header);
if (_in_msg_data) free(_in_msg_data);
return status;
}
static sgx_status_t SGX_CDECL sgx_sgxsd_enclave_server_stop(void* pms)
{
CHECK_REF_POINTER(pms, sizeof(ms_sgxsd_enclave_server_stop_t));
//
// fence after pointer checks
//
sgx_lfence();
ms_sgxsd_enclave_server_stop_t* ms = SGX_CAST(ms_sgxsd_enclave_server_stop_t*, pms);
sgx_status_t status = SGX_SUCCESS;
const sgxsd_server_terminate_args_t* _tmp_p_args = ms->ms_p_args;
size_t _len_p_args = sizeof(sgxsd_server_terminate_args_t);
sgxsd_server_terminate_args_t* _in_p_args = NULL;
CHECK_UNIQUE_POINTER(_tmp_p_args, _len_p_args);
//
// fence after pointer checks
//
sgx_lfence();
if (_tmp_p_args != NULL && _len_p_args != 0) {
_in_p_args = (sgxsd_server_terminate_args_t*)malloc(_len_p_args);
if (_in_p_args == NULL) {
status = SGX_ERROR_OUT_OF_MEMORY;
goto err;
}
if (memcpy_s(_in_p_args, _len_p_args, _tmp_p_args, _len_p_args)) {
status = SGX_ERROR_UNEXPECTED;
goto err;
}
}
ms->ms_retval = sgxsd_enclave_server_stop((const sgxsd_server_terminate_args_t*)_in_p_args, ms->ms_state_handle);
err:
if (_in_p_args) free(_in_p_args);
return status;
}
SGX_EXTERNC const struct {
size_t nr_ecall;
struct {void* ecall_addr; uint8_t is_priv;} ecall_table[8];
} g_ecall_table = {
8,
{
{(void*)(uintptr_t)sgx_kbupd_enclave_recv_untrusted_msg, 0},
{(void*)(uintptr_t)sgx_sgxsd_enclave_node_init, 0},
{(void*)(uintptr_t)sgx_sgxsd_enclave_get_next_report, 0},
{(void*)(uintptr_t)sgx_sgxsd_enclave_set_current_quote, 0},
{(void*)(uintptr_t)sgx_sgxsd_enclave_negotiate_request, 0},
{(void*)(uintptr_t)sgx_sgxsd_enclave_server_start, 0},
{(void*)(uintptr_t)sgx_sgxsd_enclave_server_call, 0},
{(void*)(uintptr_t)sgx_sgxsd_enclave_server_stop, 0},
}
};
SGX_EXTERNC const struct {
size_t nr_ocall;
uint8_t entry_table[9][8];
} g_dyn_entry_table = {
9,
{
{0, 0, 0, 0, 0, 0, 0, 0, },
{0, 0, 0, 0, 0, 0, 0, 0, },
{0, 0, 0, 0, 0, 0, 0, 0, },
{0, 0, 0, 0, 0, 0, 0, 0, },
{0, 0, 0, 0, 0, 0, 0, 0, },
{0, 0, 0, 0, 0, 0, 0, 0, },
{0, 0, 0, 0, 0, 0, 0, 0, },
{0, 0, 0, 0, 0, 0, 0, 0, },
{0, 0, 0, 0, 0, 0, 0, 0, },
}
};
sgx_status_t SGX_CDECL kbupd_enclave_ocall_recv_enclave_msg(const uint8_t* data, size_t data_size)
{
sgx_status_t status = SGX_SUCCESS;
size_t _len_data = data_size;
ms_kbupd_enclave_ocall_recv_enclave_msg_t* ms = NULL;
size_t ocalloc_size = sizeof(ms_kbupd_enclave_ocall_recv_enclave_msg_t);
void *__tmp = NULL;
CHECK_ENCLAVE_POINTER(data, _len_data);
if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (data != NULL) ? _len_data : 0))
return SGX_ERROR_INVALID_PARAMETER;
__tmp = sgx_ocalloc(ocalloc_size);
if (__tmp == NULL) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
ms = (ms_kbupd_enclave_ocall_recv_enclave_msg_t*)__tmp;
__tmp = (void *)((size_t)__tmp + sizeof(ms_kbupd_enclave_ocall_recv_enclave_msg_t));
ocalloc_size -= sizeof(ms_kbupd_enclave_ocall_recv_enclave_msg_t);
if (data != NULL) {
ms->ms_data = (const uint8_t*)__tmp;
if (_len_data % sizeof(*data) != 0) {
sgx_ocfree();
return SGX_ERROR_INVALID_PARAMETER;
}
if (memcpy_s(__tmp, ocalloc_size, data, _len_data)) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
__tmp = (void *)((size_t)__tmp + _len_data);
ocalloc_size -= _len_data;
} else {
ms->ms_data = NULL;
}
ms->ms_data_size = data_size;
status = sgx_ocall(0, ms);
if (status == SGX_SUCCESS) {
}
sgx_ocfree();
return status;
}
sgx_status_t SGX_CDECL kbupd_enclave_ocall_alloc(void** retval, size_t* size)
{
sgx_status_t status = SGX_SUCCESS;
size_t _len_size = sizeof(size_t);
ms_kbupd_enclave_ocall_alloc_t* ms = NULL;
size_t ocalloc_size = sizeof(ms_kbupd_enclave_ocall_alloc_t);
void *__tmp = NULL;
void *__tmp_size = NULL;
CHECK_ENCLAVE_POINTER(size, _len_size);
if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (size != NULL) ? _len_size : 0))
return SGX_ERROR_INVALID_PARAMETER;
__tmp = sgx_ocalloc(ocalloc_size);
if (__tmp == NULL) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
ms = (ms_kbupd_enclave_ocall_alloc_t*)__tmp;
__tmp = (void *)((size_t)__tmp + sizeof(ms_kbupd_enclave_ocall_alloc_t));
ocalloc_size -= sizeof(ms_kbupd_enclave_ocall_alloc_t);
if (size != NULL) {
ms->ms_size = (size_t*)__tmp;
__tmp_size = __tmp;
if (_len_size % sizeof(*size) != 0) {
sgx_ocfree();
return SGX_ERROR_INVALID_PARAMETER;
}
if (memcpy_s(__tmp_size, ocalloc_size, size, _len_size)) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
__tmp = (void *)((size_t)__tmp + _len_size);
ocalloc_size -= _len_size;
} else {
ms->ms_size = NULL;
}
status = sgx_ocall(1, ms);
if (status == SGX_SUCCESS) {
if (retval) *retval = ms->ms_retval;
if (size) {
if (memcpy_s((void*)size, _len_size, __tmp_size, _len_size)) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
}
}
sgx_ocfree();
return status;
}
sgx_status_t SGX_CDECL kbupd_enclave_ocall_panic(const uint8_t* msg, size_t msg_size)
{
sgx_status_t status = SGX_SUCCESS;
size_t _len_msg = msg_size;
ms_kbupd_enclave_ocall_panic_t* ms = NULL;
size_t ocalloc_size = sizeof(ms_kbupd_enclave_ocall_panic_t);
void *__tmp = NULL;
CHECK_ENCLAVE_POINTER(msg, _len_msg);
if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (msg != NULL) ? _len_msg : 0))
return SGX_ERROR_INVALID_PARAMETER;
__tmp = sgx_ocalloc(ocalloc_size);
if (__tmp == NULL) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
ms = (ms_kbupd_enclave_ocall_panic_t*)__tmp;
__tmp = (void *)((size_t)__tmp + sizeof(ms_kbupd_enclave_ocall_panic_t));
ocalloc_size -= sizeof(ms_kbupd_enclave_ocall_panic_t);
if (msg != NULL) {
ms->ms_msg = (const uint8_t*)__tmp;
if (_len_msg % sizeof(*msg) != 0) {
sgx_ocfree();
return SGX_ERROR_INVALID_PARAMETER;
}
if (memcpy_s(__tmp, ocalloc_size, msg, _len_msg)) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
__tmp = (void *)((size_t)__tmp + _len_msg);
ocalloc_size -= _len_msg;
} else {
ms->ms_msg = NULL;
}
ms->ms_msg_size = msg_size;
status = sgx_ocall(2, ms);
if (status == SGX_SUCCESS) {
}
sgx_ocfree();
return status;
}
sgx_status_t SGX_CDECL sgxsd_ocall_reply(sgx_status_t* retval, const sgxsd_msg_header_t* reply_header, const uint8_t* reply_data, size_t reply_data_size, sgxsd_msg_tag_t msg_tag)
{
sgx_status_t status = SGX_SUCCESS;
size_t _len_reply_header = sizeof(sgxsd_msg_header_t);
size_t _len_reply_data = reply_data_size;
ms_sgxsd_ocall_reply_t* ms = NULL;
size_t ocalloc_size = sizeof(ms_sgxsd_ocall_reply_t);
void *__tmp = NULL;
CHECK_ENCLAVE_POINTER(reply_header, _len_reply_header);
CHECK_ENCLAVE_POINTER(reply_data, _len_reply_data);
if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (reply_header != NULL) ? _len_reply_header : 0))
return SGX_ERROR_INVALID_PARAMETER;
if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (reply_data != NULL) ? _len_reply_data : 0))
return SGX_ERROR_INVALID_PARAMETER;
__tmp = sgx_ocalloc(ocalloc_size);
if (__tmp == NULL) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
ms = (ms_sgxsd_ocall_reply_t*)__tmp;
__tmp = (void *)((size_t)__tmp + sizeof(ms_sgxsd_ocall_reply_t));
ocalloc_size -= sizeof(ms_sgxsd_ocall_reply_t);
if (reply_header != NULL) {
ms->ms_reply_header = (const sgxsd_msg_header_t*)__tmp;
if (memcpy_s(__tmp, ocalloc_size, reply_header, _len_reply_header)) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
__tmp = (void *)((size_t)__tmp + _len_reply_header);
ocalloc_size -= _len_reply_header;
} else {
ms->ms_reply_header = NULL;
}
if (reply_data != NULL) {
ms->ms_reply_data = (const uint8_t*)__tmp;
if (_len_reply_data % sizeof(*reply_data) != 0) {
sgx_ocfree();
return SGX_ERROR_INVALID_PARAMETER;
}
if (memcpy_s(__tmp, ocalloc_size, reply_data, _len_reply_data)) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
__tmp = (void *)((size_t)__tmp + _len_reply_data);
ocalloc_size -= _len_reply_data;
} else {
ms->ms_reply_data = NULL;
}
ms->ms_reply_data_size = reply_data_size;
ms->ms_msg_tag = msg_tag;
status = sgx_ocall(3, ms);
if (status == SGX_SUCCESS) {
if (retval) *retval = ms->ms_retval;
}
sgx_ocfree();
return status;
}
sgx_status_t SGX_CDECL sgx_oc_cpuidex(int cpuinfo[4], int leaf, int subleaf)
{
sgx_status_t status = SGX_SUCCESS;
size_t _len_cpuinfo = 4 * sizeof(int);
ms_sgx_oc_cpuidex_t* ms = NULL;
size_t ocalloc_size = sizeof(ms_sgx_oc_cpuidex_t);
void *__tmp = NULL;
void *__tmp_cpuinfo = NULL;
CHECK_ENCLAVE_POINTER(cpuinfo, _len_cpuinfo);
if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (cpuinfo != NULL) ? _len_cpuinfo : 0))
return SGX_ERROR_INVALID_PARAMETER;
__tmp = sgx_ocalloc(ocalloc_size);
if (__tmp == NULL) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
ms = (ms_sgx_oc_cpuidex_t*)__tmp;
__tmp = (void *)((size_t)__tmp + sizeof(ms_sgx_oc_cpuidex_t));
ocalloc_size -= sizeof(ms_sgx_oc_cpuidex_t);
if (cpuinfo != NULL) {
ms->ms_cpuinfo = (int*)__tmp;
__tmp_cpuinfo = __tmp;
if (_len_cpuinfo % sizeof(*cpuinfo) != 0) {
sgx_ocfree();
return SGX_ERROR_INVALID_PARAMETER;
}
memset(__tmp_cpuinfo, 0, _len_cpuinfo);
__tmp = (void *)((size_t)__tmp + _len_cpuinfo);
ocalloc_size -= _len_cpuinfo;
} else {
ms->ms_cpuinfo = NULL;
}
ms->ms_leaf = leaf;
ms->ms_subleaf = subleaf;
status = sgx_ocall(4, ms);
if (status == SGX_SUCCESS) {
if (cpuinfo) {
if (memcpy_s((void*)cpuinfo, _len_cpuinfo, __tmp_cpuinfo, _len_cpuinfo)) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
}
}
sgx_ocfree();
return status;
}
sgx_status_t SGX_CDECL sgx_thread_wait_untrusted_event_ocall(int* retval, const void* self)
{
sgx_status_t status = SGX_SUCCESS;
ms_sgx_thread_wait_untrusted_event_ocall_t* ms = NULL;
size_t ocalloc_size = sizeof(ms_sgx_thread_wait_untrusted_event_ocall_t);
void *__tmp = NULL;
__tmp = sgx_ocalloc(ocalloc_size);
if (__tmp == NULL) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
ms = (ms_sgx_thread_wait_untrusted_event_ocall_t*)__tmp;
__tmp = (void *)((size_t)__tmp + sizeof(ms_sgx_thread_wait_untrusted_event_ocall_t));
ocalloc_size -= sizeof(ms_sgx_thread_wait_untrusted_event_ocall_t);
ms->ms_self = self;
status = sgx_ocall(5, ms);
if (status == SGX_SUCCESS) {
if (retval) *retval = ms->ms_retval;
}
sgx_ocfree();
return status;
}
sgx_status_t SGX_CDECL sgx_thread_set_untrusted_event_ocall(int* retval, const void* waiter)
{
sgx_status_t status = SGX_SUCCESS;
ms_sgx_thread_set_untrusted_event_ocall_t* ms = NULL;
size_t ocalloc_size = sizeof(ms_sgx_thread_set_untrusted_event_ocall_t);
void *__tmp = NULL;
__tmp = sgx_ocalloc(ocalloc_size);
if (__tmp == NULL) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
ms = (ms_sgx_thread_set_untrusted_event_ocall_t*)__tmp;
__tmp = (void *)((size_t)__tmp + sizeof(ms_sgx_thread_set_untrusted_event_ocall_t));
ocalloc_size -= sizeof(ms_sgx_thread_set_untrusted_event_ocall_t);
ms->ms_waiter = waiter;
status = sgx_ocall(6, ms);
if (status == SGX_SUCCESS) {
if (retval) *retval = ms->ms_retval;
}
sgx_ocfree();
return status;
}
sgx_status_t SGX_CDECL sgx_thread_setwait_untrusted_events_ocall(int* retval, const void* waiter, const void* self)
{
sgx_status_t status = SGX_SUCCESS;
ms_sgx_thread_setwait_untrusted_events_ocall_t* ms = NULL;
size_t ocalloc_size = sizeof(ms_sgx_thread_setwait_untrusted_events_ocall_t);
void *__tmp = NULL;
__tmp = sgx_ocalloc(ocalloc_size);
if (__tmp == NULL) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
ms = (ms_sgx_thread_setwait_untrusted_events_ocall_t*)__tmp;
__tmp = (void *)((size_t)__tmp + sizeof(ms_sgx_thread_setwait_untrusted_events_ocall_t));
ocalloc_size -= sizeof(ms_sgx_thread_setwait_untrusted_events_ocall_t);
ms->ms_waiter = waiter;
ms->ms_self = self;
status = sgx_ocall(7, ms);
if (status == SGX_SUCCESS) {
if (retval) *retval = ms->ms_retval;
}
sgx_ocfree();
return status;
}
sgx_status_t SGX_CDECL sgx_thread_set_multiple_untrusted_events_ocall(int* retval, const void** waiters, size_t total)
{
sgx_status_t status = SGX_SUCCESS;
size_t _len_waiters = total * sizeof(void*);
ms_sgx_thread_set_multiple_untrusted_events_ocall_t* ms = NULL;
size_t ocalloc_size = sizeof(ms_sgx_thread_set_multiple_untrusted_events_ocall_t);
void *__tmp = NULL;
CHECK_ENCLAVE_POINTER(waiters, _len_waiters);
if (ADD_ASSIGN_OVERFLOW(ocalloc_size, (waiters != NULL) ? _len_waiters : 0))
return SGX_ERROR_INVALID_PARAMETER;
__tmp = sgx_ocalloc(ocalloc_size);
if (__tmp == NULL) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
ms = (ms_sgx_thread_set_multiple_untrusted_events_ocall_t*)__tmp;
__tmp = (void *)((size_t)__tmp + sizeof(ms_sgx_thread_set_multiple_untrusted_events_ocall_t));
ocalloc_size -= sizeof(ms_sgx_thread_set_multiple_untrusted_events_ocall_t);
if (waiters != NULL) {
ms->ms_waiters = (const void**)__tmp;
if (_len_waiters % sizeof(*waiters) != 0) {
sgx_ocfree();
return SGX_ERROR_INVALID_PARAMETER;
}
if (memcpy_s(__tmp, ocalloc_size, waiters, _len_waiters)) {
sgx_ocfree();
return SGX_ERROR_UNEXPECTED;
}
__tmp = (void *)((size_t)__tmp + _len_waiters);
ocalloc_size -= _len_waiters;
} else {
ms->ms_waiters = NULL;
}
ms->ms_total = total;
status = sgx_ocall(8, ms);
if (status == SGX_SUCCESS) {
if (retval) *retval = ms->ms_retval;
}
sgx_ocfree();
return status;
}

View File

@ -0,0 +1,46 @@
#ifndef KBUPD_ENCLAVE_T_H__
#define KBUPD_ENCLAVE_T_H__
#include <stdint.h>
#include <wchar.h>
#include <stddef.h>
#include "sgx_edger8r.h" /* for sgx_ocall etc. */
#include "kbupd_sgxsd_callbacks.h"
#include "stdbool.h"
#include "sgx_quote.h"
#include "sgx_report.h"
#include "sgxsd.h"
#include <stdlib.h> /* for size_t */
#define SGX_CAST(type, item) ((type)(item))
#ifdef __cplusplus
extern "C" {
#endif
void kbupd_enclave_recv_untrusted_msg(const uint8_t* data, size_t data_size);
sgx_status_t sgxsd_enclave_node_init(const sgxsd_node_init_args_t* p_args);
sgx_status_t sgxsd_enclave_get_next_report(sgx_target_info_t qe_target_info, sgx_report_t* p_report);
sgx_status_t sgxsd_enclave_set_current_quote(void);
sgx_status_t sgxsd_enclave_negotiate_request(const sgxsd_request_negotiation_request_t* p_request, sgxsd_request_negotiation_response_t* p_response);
sgx_status_t sgxsd_enclave_server_start(const sgxsd_server_init_args_t* p_args, sgxsd_server_state_handle_t state_handle);
sgx_status_t sgxsd_enclave_server_call(const sgxsd_server_handle_call_args_t* p_args, const sgxsd_msg_header_t* msg_header, uint8_t* msg_data, size_t msg_size, sgxsd_msg_tag_t msg_tag, sgxsd_server_state_handle_t state_handle);
sgx_status_t sgxsd_enclave_server_stop(const sgxsd_server_terminate_args_t* p_args, sgxsd_server_state_handle_t state_handle);
sgx_status_t SGX_CDECL kbupd_enclave_ocall_recv_enclave_msg(const uint8_t* data, size_t data_size);
sgx_status_t SGX_CDECL kbupd_enclave_ocall_alloc(void** retval, size_t* size);
sgx_status_t SGX_CDECL kbupd_enclave_ocall_panic(const uint8_t* msg, size_t msg_size);
sgx_status_t SGX_CDECL sgxsd_ocall_reply(sgx_status_t* retval, const sgxsd_msg_header_t* reply_header, const uint8_t* reply_data, size_t reply_data_size, sgxsd_msg_tag_t msg_tag);
sgx_status_t SGX_CDECL sgx_oc_cpuidex(int cpuinfo[4], int leaf, int subleaf);
sgx_status_t SGX_CDECL sgx_thread_wait_untrusted_event_ocall(int* retval, const void* self);
sgx_status_t SGX_CDECL sgx_thread_set_untrusted_event_ocall(int* retval, const void* waiter);
sgx_status_t SGX_CDECL sgx_thread_setwait_untrusted_events_ocall(int* retval, const void* waiter, const void* self);
sgx_status_t SGX_CDECL sgx_thread_set_multiple_untrusted_events_ocall(int* retval, const void** waiters, size_t total);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif

View File

@ -0,0 +1,279 @@
#include "kbupd_enclave_u.h"
#include <errno.h>
typedef struct ms_kbupd_enclave_recv_untrusted_msg_t {
const uint8_t* ms_data;
size_t ms_data_size;
} ms_kbupd_enclave_recv_untrusted_msg_t;
typedef struct ms_sgxsd_enclave_node_init_t {
sgx_status_t ms_retval;
const sgxsd_node_init_args_t* ms_p_args;
} ms_sgxsd_enclave_node_init_t;
typedef struct ms_sgxsd_enclave_get_next_report_t {
sgx_status_t ms_retval;
sgx_target_info_t ms_qe_target_info;
sgx_report_t* ms_p_report;
} ms_sgxsd_enclave_get_next_report_t;
typedef struct ms_sgxsd_enclave_set_current_quote_t {
sgx_status_t ms_retval;
} ms_sgxsd_enclave_set_current_quote_t;
typedef struct ms_sgxsd_enclave_negotiate_request_t {
sgx_status_t ms_retval;
const sgxsd_request_negotiation_request_t* ms_p_request;
sgxsd_request_negotiation_response_t* ms_p_response;
} ms_sgxsd_enclave_negotiate_request_t;
typedef struct ms_sgxsd_enclave_server_start_t {
sgx_status_t ms_retval;
const sgxsd_server_init_args_t* ms_p_args;
sgxsd_server_state_handle_t ms_state_handle;
} ms_sgxsd_enclave_server_start_t;
typedef struct ms_sgxsd_enclave_server_call_t {
sgx_status_t ms_retval;
const sgxsd_server_handle_call_args_t* ms_p_args;
const sgxsd_msg_header_t* ms_msg_header;
uint8_t* ms_msg_data;
size_t ms_msg_size;
sgxsd_msg_tag_t ms_msg_tag;
sgxsd_server_state_handle_t ms_state_handle;
} ms_sgxsd_enclave_server_call_t;
typedef struct ms_sgxsd_enclave_server_stop_t {
sgx_status_t ms_retval;
const sgxsd_server_terminate_args_t* ms_p_args;
sgxsd_server_state_handle_t ms_state_handle;
} ms_sgxsd_enclave_server_stop_t;
typedef struct ms_kbupd_enclave_ocall_recv_enclave_msg_t {
const uint8_t* ms_data;
size_t ms_data_size;
} ms_kbupd_enclave_ocall_recv_enclave_msg_t;
typedef struct ms_kbupd_enclave_ocall_alloc_t {
void* ms_retval;
size_t* ms_size;
} ms_kbupd_enclave_ocall_alloc_t;
typedef struct ms_kbupd_enclave_ocall_panic_t {
const uint8_t* ms_msg;
size_t ms_msg_size;
} ms_kbupd_enclave_ocall_panic_t;
typedef struct ms_sgxsd_ocall_reply_t {
sgx_status_t ms_retval;
const sgxsd_msg_header_t* ms_reply_header;
const uint8_t* ms_reply_data;
size_t ms_reply_data_size;
sgxsd_msg_tag_t ms_msg_tag;
} ms_sgxsd_ocall_reply_t;
typedef struct ms_sgx_oc_cpuidex_t {
int* ms_cpuinfo;
int ms_leaf;
int ms_subleaf;
} ms_sgx_oc_cpuidex_t;
typedef struct ms_sgx_thread_wait_untrusted_event_ocall_t {
int ms_retval;
const void* ms_self;
} ms_sgx_thread_wait_untrusted_event_ocall_t;
typedef struct ms_sgx_thread_set_untrusted_event_ocall_t {
int ms_retval;
const void* ms_waiter;
} ms_sgx_thread_set_untrusted_event_ocall_t;
typedef struct ms_sgx_thread_setwait_untrusted_events_ocall_t {
int ms_retval;
const void* ms_waiter;
const void* ms_self;
} ms_sgx_thread_setwait_untrusted_events_ocall_t;
typedef struct ms_sgx_thread_set_multiple_untrusted_events_ocall_t {
int ms_retval;
const void** ms_waiters;
size_t ms_total;
} ms_sgx_thread_set_multiple_untrusted_events_ocall_t;
static sgx_status_t SGX_CDECL kbupd_enclave_kbupd_enclave_ocall_recv_enclave_msg(void* pms)
{
ms_kbupd_enclave_ocall_recv_enclave_msg_t* ms = SGX_CAST(ms_kbupd_enclave_ocall_recv_enclave_msg_t*, pms);
kbupd_enclave_ocall_recv_enclave_msg(ms->ms_data, ms->ms_data_size);
return SGX_SUCCESS;
}
static sgx_status_t SGX_CDECL kbupd_enclave_kbupd_enclave_ocall_alloc(void* pms)
{
ms_kbupd_enclave_ocall_alloc_t* ms = SGX_CAST(ms_kbupd_enclave_ocall_alloc_t*, pms);
ms->ms_retval = kbupd_enclave_ocall_alloc(ms->ms_size);
return SGX_SUCCESS;
}
static sgx_status_t SGX_CDECL kbupd_enclave_kbupd_enclave_ocall_panic(void* pms)
{
ms_kbupd_enclave_ocall_panic_t* ms = SGX_CAST(ms_kbupd_enclave_ocall_panic_t*, pms);
kbupd_enclave_ocall_panic(ms->ms_msg, ms->ms_msg_size);
return SGX_SUCCESS;
}
static sgx_status_t SGX_CDECL kbupd_enclave_sgxsd_ocall_reply(void* pms)
{
ms_sgxsd_ocall_reply_t* ms = SGX_CAST(ms_sgxsd_ocall_reply_t*, pms);
ms->ms_retval = sgxsd_ocall_reply(ms->ms_reply_header, ms->ms_reply_data, ms->ms_reply_data_size, ms->ms_msg_tag);
return SGX_SUCCESS;
}
static sgx_status_t SGX_CDECL kbupd_enclave_sgx_oc_cpuidex(void* pms)
{
ms_sgx_oc_cpuidex_t* ms = SGX_CAST(ms_sgx_oc_cpuidex_t*, pms);
sgx_oc_cpuidex(ms->ms_cpuinfo, ms->ms_leaf, ms->ms_subleaf);
return SGX_SUCCESS;
}
static sgx_status_t SGX_CDECL kbupd_enclave_sgx_thread_wait_untrusted_event_ocall(void* pms)
{
ms_sgx_thread_wait_untrusted_event_ocall_t* ms = SGX_CAST(ms_sgx_thread_wait_untrusted_event_ocall_t*, pms);
ms->ms_retval = sgx_thread_wait_untrusted_event_ocall(ms->ms_self);
return SGX_SUCCESS;
}
static sgx_status_t SGX_CDECL kbupd_enclave_sgx_thread_set_untrusted_event_ocall(void* pms)
{
ms_sgx_thread_set_untrusted_event_ocall_t* ms = SGX_CAST(ms_sgx_thread_set_untrusted_event_ocall_t*, pms);
ms->ms_retval = sgx_thread_set_untrusted_event_ocall(ms->ms_waiter);
return SGX_SUCCESS;
}
static sgx_status_t SGX_CDECL kbupd_enclave_sgx_thread_setwait_untrusted_events_ocall(void* pms)
{
ms_sgx_thread_setwait_untrusted_events_ocall_t* ms = SGX_CAST(ms_sgx_thread_setwait_untrusted_events_ocall_t*, pms);
ms->ms_retval = sgx_thread_setwait_untrusted_events_ocall(ms->ms_waiter, ms->ms_self);
return SGX_SUCCESS;
}
static sgx_status_t SGX_CDECL kbupd_enclave_sgx_thread_set_multiple_untrusted_events_ocall(void* pms)
{
ms_sgx_thread_set_multiple_untrusted_events_ocall_t* ms = SGX_CAST(ms_sgx_thread_set_multiple_untrusted_events_ocall_t*, pms);
ms->ms_retval = sgx_thread_set_multiple_untrusted_events_ocall(ms->ms_waiters, ms->ms_total);
return SGX_SUCCESS;
}
static const struct {
size_t nr_ocall;
void * table[9];
} ocall_table_kbupd_enclave = {
9,
{
(void*)kbupd_enclave_kbupd_enclave_ocall_recv_enclave_msg,
(void*)kbupd_enclave_kbupd_enclave_ocall_alloc,
(void*)kbupd_enclave_kbupd_enclave_ocall_panic,
(void*)kbupd_enclave_sgxsd_ocall_reply,
(void*)kbupd_enclave_sgx_oc_cpuidex,
(void*)kbupd_enclave_sgx_thread_wait_untrusted_event_ocall,
(void*)kbupd_enclave_sgx_thread_set_untrusted_event_ocall,
(void*)kbupd_enclave_sgx_thread_setwait_untrusted_events_ocall,
(void*)kbupd_enclave_sgx_thread_set_multiple_untrusted_events_ocall,
}
};
sgx_status_t kbupd_enclave_recv_untrusted_msg(sgx_enclave_id_t eid, const uint8_t* data, size_t data_size)
{
sgx_status_t status;
ms_kbupd_enclave_recv_untrusted_msg_t ms;
ms.ms_data = data;
ms.ms_data_size = data_size;
status = sgx_ecall(eid, 0, &ocall_table_kbupd_enclave, &ms);
return status;
}
sgx_status_t sgxsd_enclave_node_init(sgx_enclave_id_t eid, sgx_status_t* retval, const sgxsd_node_init_args_t* p_args)
{
sgx_status_t status;
ms_sgxsd_enclave_node_init_t ms;
ms.ms_p_args = p_args;
status = sgx_ecall(eid, 1, &ocall_table_kbupd_enclave, &ms);
if (status == SGX_SUCCESS && retval) *retval = ms.ms_retval;
return status;
}
sgx_status_t sgxsd_enclave_get_next_report(sgx_enclave_id_t eid, sgx_status_t* retval, sgx_target_info_t qe_target_info, sgx_report_t* p_report)
{
sgx_status_t status;
ms_sgxsd_enclave_get_next_report_t ms;
ms.ms_qe_target_info = qe_target_info;
ms.ms_p_report = p_report;
status = sgx_ecall(eid, 2, &ocall_table_kbupd_enclave, &ms);
if (status == SGX_SUCCESS && retval) *retval = ms.ms_retval;
return status;
}
sgx_status_t sgxsd_enclave_set_current_quote(sgx_enclave_id_t eid, sgx_status_t* retval)
{
sgx_status_t status;
ms_sgxsd_enclave_set_current_quote_t ms;
status = sgx_ecall(eid, 3, &ocall_table_kbupd_enclave, &ms);
if (status == SGX_SUCCESS && retval) *retval = ms.ms_retval;
return status;
}
sgx_status_t sgxsd_enclave_negotiate_request(sgx_enclave_id_t eid, sgx_status_t* retval, const sgxsd_request_negotiation_request_t* p_request, sgxsd_request_negotiation_response_t* p_response)
{
sgx_status_t status;
ms_sgxsd_enclave_negotiate_request_t ms;
ms.ms_p_request = p_request;
ms.ms_p_response = p_response;
status = sgx_ecall(eid, 4, &ocall_table_kbupd_enclave, &ms);
if (status == SGX_SUCCESS && retval) *retval = ms.ms_retval;
return status;
}
sgx_status_t sgxsd_enclave_server_start(sgx_enclave_id_t eid, sgx_status_t* retval, const sgxsd_server_init_args_t* p_args, sgxsd_server_state_handle_t state_handle)
{
sgx_status_t status;
ms_sgxsd_enclave_server_start_t ms;
ms.ms_p_args = p_args;
ms.ms_state_handle = state_handle;
status = sgx_ecall(eid, 5, &ocall_table_kbupd_enclave, &ms);
if (status == SGX_SUCCESS && retval) *retval = ms.ms_retval;
return status;
}
sgx_status_t sgxsd_enclave_server_call(sgx_enclave_id_t eid, sgx_status_t* retval, const sgxsd_server_handle_call_args_t* p_args, const sgxsd_msg_header_t* msg_header, uint8_t* msg_data, size_t msg_size, sgxsd_msg_tag_t msg_tag, sgxsd_server_state_handle_t state_handle)
{
sgx_status_t status;
ms_sgxsd_enclave_server_call_t ms;
ms.ms_p_args = p_args;
ms.ms_msg_header = msg_header;
ms.ms_msg_data = msg_data;
ms.ms_msg_size = msg_size;
ms.ms_msg_tag = msg_tag;
ms.ms_state_handle = state_handle;
status = sgx_ecall(eid, 6, &ocall_table_kbupd_enclave, &ms);
if (status == SGX_SUCCESS && retval) *retval = ms.ms_retval;
return status;
}
sgx_status_t sgxsd_enclave_server_stop(sgx_enclave_id_t eid, sgx_status_t* retval, const sgxsd_server_terminate_args_t* p_args, sgxsd_server_state_handle_t state_handle)
{
sgx_status_t status;
ms_sgxsd_enclave_server_stop_t ms;
ms.ms_p_args = p_args;
ms.ms_state_handle = state_handle;
status = sgx_ecall(eid, 7, &ocall_table_kbupd_enclave, &ms);
if (status == SGX_SUCCESS && retval) *retval = ms.ms_retval;
return status;
}

View File

@ -0,0 +1,74 @@
#ifndef KBUPD_ENCLAVE_U_H__
#define KBUPD_ENCLAVE_U_H__
#include <stdint.h>
#include <wchar.h>
#include <stddef.h>
#include <string.h>
#include "sgx_edger8r.h" /* for sgx_status_t etc. */
#include "kbupd_sgxsd_callbacks.h"
#include "stdbool.h"
#include "sgx_quote.h"
#include "sgx_report.h"
#include "sgxsd.h"
#include <stdlib.h> /* for size_t */
#define SGX_CAST(type, item) ((type)(item))
#ifdef __cplusplus
extern "C" {
#endif
#ifndef KBUPD_ENCLAVE_OCALL_RECV_ENCLAVE_MSG_DEFINED__
#define KBUPD_ENCLAVE_OCALL_RECV_ENCLAVE_MSG_DEFINED__
void SGX_UBRIDGE(SGX_NOCONVENTION, kbupd_enclave_ocall_recv_enclave_msg, (const uint8_t* data, size_t data_size));
#endif
#ifndef KBUPD_ENCLAVE_OCALL_ALLOC_DEFINED__
#define KBUPD_ENCLAVE_OCALL_ALLOC_DEFINED__
void* SGX_UBRIDGE(SGX_NOCONVENTION, kbupd_enclave_ocall_alloc, (size_t* size));
#endif
#ifndef KBUPD_ENCLAVE_OCALL_PANIC_DEFINED__
#define KBUPD_ENCLAVE_OCALL_PANIC_DEFINED__
void SGX_UBRIDGE(SGX_NOCONVENTION, kbupd_enclave_ocall_panic, (const uint8_t* msg, size_t msg_size));
#endif
#ifndef SGXSD_OCALL_REPLY_DEFINED__
#define SGXSD_OCALL_REPLY_DEFINED__
sgx_status_t SGX_UBRIDGE(SGX_NOCONVENTION, sgxsd_ocall_reply, (const sgxsd_msg_header_t* reply_header, const uint8_t* reply_data, size_t reply_data_size, sgxsd_msg_tag_t msg_tag));
#endif
#ifndef SGX_OC_CPUIDEX_DEFINED__
#define SGX_OC_CPUIDEX_DEFINED__
void SGX_UBRIDGE(SGX_CDECL, sgx_oc_cpuidex, (int cpuinfo[4], int leaf, int subleaf));
#endif
#ifndef SGX_THREAD_WAIT_UNTRUSTED_EVENT_OCALL_DEFINED__
#define SGX_THREAD_WAIT_UNTRUSTED_EVENT_OCALL_DEFINED__
int SGX_UBRIDGE(SGX_CDECL, sgx_thread_wait_untrusted_event_ocall, (const void* self));
#endif
#ifndef SGX_THREAD_SET_UNTRUSTED_EVENT_OCALL_DEFINED__
#define SGX_THREAD_SET_UNTRUSTED_EVENT_OCALL_DEFINED__
int SGX_UBRIDGE(SGX_CDECL, sgx_thread_set_untrusted_event_ocall, (const void* waiter));
#endif
#ifndef SGX_THREAD_SETWAIT_UNTRUSTED_EVENTS_OCALL_DEFINED__
#define SGX_THREAD_SETWAIT_UNTRUSTED_EVENTS_OCALL_DEFINED__
int SGX_UBRIDGE(SGX_CDECL, sgx_thread_setwait_untrusted_events_ocall, (const void* waiter, const void* self));
#endif
#ifndef SGX_THREAD_SET_MULTIPLE_UNTRUSTED_EVENTS_OCALL_DEFINED__
#define SGX_THREAD_SET_MULTIPLE_UNTRUSTED_EVENTS_OCALL_DEFINED__
int SGX_UBRIDGE(SGX_CDECL, sgx_thread_set_multiple_untrusted_events_ocall, (const void** waiters, size_t total));
#endif
sgx_status_t kbupd_enclave_recv_untrusted_msg(sgx_enclave_id_t eid, const uint8_t* data, size_t data_size);
sgx_status_t sgxsd_enclave_node_init(sgx_enclave_id_t eid, sgx_status_t* retval, const sgxsd_node_init_args_t* p_args);
sgx_status_t sgxsd_enclave_get_next_report(sgx_enclave_id_t eid, sgx_status_t* retval, sgx_target_info_t qe_target_info, sgx_report_t* p_report);
sgx_status_t sgxsd_enclave_set_current_quote(sgx_enclave_id_t eid, sgx_status_t* retval);
sgx_status_t sgxsd_enclave_negotiate_request(sgx_enclave_id_t eid, sgx_status_t* retval, const sgxsd_request_negotiation_request_t* p_request, sgxsd_request_negotiation_response_t* p_response);
sgx_status_t sgxsd_enclave_server_start(sgx_enclave_id_t eid, sgx_status_t* retval, const sgxsd_server_init_args_t* p_args, sgxsd_server_state_handle_t state_handle);
sgx_status_t sgxsd_enclave_server_call(sgx_enclave_id_t eid, sgx_status_t* retval, const sgxsd_server_handle_call_args_t* p_args, const sgxsd_msg_header_t* msg_header, uint8_t* msg_data, size_t msg_size, sgxsd_msg_tag_t msg_tag, sgxsd_server_state_handle_t state_handle);
sgx_status_t sgxsd_enclave_server_stop(sgx_enclave_id_t eid, sgx_status_t* retval, const sgxsd_server_terminate_args_t* p_args, sgxsd_server_state_handle_t state_handle);
#ifdef __cplusplus
}
#endif /* __cplusplus */
#endif

View File

@ -0,0 +1,31 @@
#ifndef _KBUPD_SGXSD_CALLBACKS_H
#define _KBUPD_SGXSD_CALLBACKS_H
#include <stdint.h>
typedef enum kbupd_request_type {
KBUPD_REQUEST_TYPE_ANY = 0,
KBUPD_REQUEST_TYPE_BACKUP = 1,
KBUPD_REQUEST_TYPE_RESTORE = 2,
KBUPD_REQUEST_TYPE_DELETE = 3,
} kbupd_request_type_t;
typedef struct sgxsd_server_init_args {
} sgxsd_server_init_args_t;
_Static_assert(sizeof(sgxsd_server_init_args_t) == 0, "Enclave ABI compatibility");
typedef struct sgxsd_server_handle_call_args {
uint8_t backup_id[32];
uint32_t request_type;
} sgxsd_server_handle_call_args_t;
_Static_assert(sizeof(sgxsd_server_handle_call_args_t) == 36, "Enclave ABI compatibility");
typedef struct sgxsd_server_terminate_args {
} sgxsd_server_terminate_args_t;
_Static_assert(sizeof(sgxsd_server_terminate_args_t) == 0, "Enclave ABI compatibility");
typedef struct sgxsd_ra_get_quote_args {
const void *args;
} sgxsd_ra_get_quote_args_t;
#endif

View File

@ -0,0 +1,97 @@
/*
* Copyright (C) 2017 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Jeff Griffin
*/
#ifndef _SGXSD_ENCLAVE_H
#define _SGXSD_ENCLAVE_H
#include "sgx_error.h"
#include "sgxsd.h"
typedef struct sgxsd_msg_buf {
uint8_t *data;
uint32_t size;
} sgxsd_msg_buf_t;
typedef struct sgxsd_msg_from {
bool valid;
sgxsd_msg_tag_t tag;
sgxsd_aes_gcm_key_t server_key;
} sgxsd_msg_from_t;
//
// callbacks
//
// the incomplete type sgxsd_server_state doesn't necessarily need to be defined
typedef struct sgxsd_server_state sgxsd_server_state_t;
/* the incomplete types sgxsd_server_{init,handle_call,terminate}_args must be defined and included before sgxsd APIs
in the .edl file */
typedef struct sgxsd_server_init_args sgxsd_server_init_args_t;
typedef struct sgxsd_server_handle_call_args sgxsd_server_handle_call_args_t;
typedef struct sgxsd_server_terminate_args sgxsd_server_terminate_args_t;
// the callbacks sgxsd_enclave_server_{init,handle_call,terminate} handle sgxsd_enclave_server_{start,call,stop} calls
sgx_status_t sgxsd_enclave_server_init(const sgxsd_server_init_args_t *p_args, sgxsd_server_state_t **pp_state);
sgx_status_t sgxsd_enclave_server_handle_call(const sgxsd_server_handle_call_args_t *p_args, sgxsd_msg_buf_t msg, sgxsd_msg_from_t from, sgxsd_server_state_t **pp_state);
sgx_status_t sgxsd_enclave_server_terminate(const sgxsd_server_terminate_args_t *p_args, sgxsd_server_state_t *p_state);
//
// public api
//
// sgxsd_enclave_server_reply sends a reply to the message corresponding to the given sgxsd_msg_from from handle_call
sgx_status_t sgxsd_enclave_server_reply(sgxsd_msg_buf_t reply_buf, sgxsd_msg_from_t *p_from);
sgx_status_t sgxsd_enclave_server_noreply(sgxsd_msg_from_t *p_from);
//
// public utility apis
//
sgx_status_t sgxsd_aes_gcm_encrypt(const sgxsd_aes_gcm_key_t *p_key,
const void *p_src, uint32_t src_len, void *p_dst,
const sgxsd_aes_gcm_iv_t *p_iv,
const void *p_aad, uint32_t aad_len,
sgxsd_aes_gcm_mac_t *p_out_mac);
sgx_status_t sgxsd_aes_gcm_decrypt(const sgxsd_aes_gcm_key_t *p_key,
const void *p_src, uint32_t src_len, void *p_dst,
const sgxsd_aes_gcm_iv_t *p_iv,
const void *p_aad, uint32_t aad_len,
const sgxsd_aes_gcm_mac_t *p_in_mac);
typedef struct sgxsd_rand_buf {
uint8_t x[SGXSD_CURVE25519_KEY_SIZE];
} sgxsd_rand_buf_t, sgxsd_curve25519_private_key_t;
sgx_status_t sgxsd_enclave_read_rand(sgxsd_rand_buf_t *p_privkey);
//
// internal definitions
//
typedef struct sgxsd_curve25519_key_pair {
sgxsd_curve25519_private_key_t privkey;
sgxsd_curve25519_public_key_t pubkey;
} sgxsd_curve25519_key_pair_t;
#endif

36
enclave/include/sgxsd.edl Normal file
View File

@ -0,0 +1,36 @@
enclave {
include "stdbool.h"
include "sgx_quote.h"
include "sgx_report.h"
include "sgxsd.h"
trusted {
public sgx_status_t sgxsd_enclave_node_init([in] const sgxsd_node_init_args_t *p_args);
public sgx_status_t sgxsd_enclave_get_next_report
(sgx_target_info_t qe_target_info, [out] sgx_report_t *p_report);
public sgx_status_t sgxsd_enclave_set_current_quote();
public sgx_status_t sgxsd_enclave_negotiate_request
([in] const sgxsd_request_negotiation_request_t *p_request,
[out] sgxsd_request_negotiation_response_t *p_response);
public sgx_status_t sgxsd_enclave_server_start
([in] const sgxsd_server_init_args_t *p_args,
sgxsd_server_state_handle_t state_handle);
public sgx_status_t sgxsd_enclave_server_call
([in] const sgxsd_server_handle_call_args_t *p_args,
[in] const sgxsd_msg_header_t *msg_header,
[in, size=msg_size] uint8_t *msg_data, size_t msg_size,
sgxsd_msg_tag_t msg_tag, sgxsd_server_state_handle_t state_handle);
public sgx_status_t sgxsd_enclave_server_stop
([in] const sgxsd_server_terminate_args_t *p_args,
sgxsd_server_state_handle_t state_handle);
};
untrusted {
sgx_status_t sgxsd_ocall_reply
([in] const sgxsd_msg_header_t *reply_header,
[in, size=reply_data_size] const uint8_t *reply_data, size_t reply_data_size,
sgxsd_msg_tag_t msg_tag);
};
};

139
enclave/include/sgxsd.h Normal file
View File

@ -0,0 +1,139 @@
/*
* Copyright (C) 2017 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @author Jeff Griffin
*/
#ifndef _SGXSD_H
#define _SGXSD_H
#include <stdbool.h>
#include <stdarg.h>
#include <stdlib.h>
#include "sgx_urts.h"
#include "sgx_quote.h"
//
// types
//
#define SGXSD_AES_GCM_IV_SIZE 12
#define SGXSD_AES_GCM_MAC_SIZE 16
#define SGXSD_AES_GCM_KEY_SIZE 32
#define SGXSD_CURVE25519_KEY_SIZE 32
#define SGXSD_SHA256_HASH_SIZE 32
typedef struct sgxsd_aes_gcm_mac {
uint8_t data[SGXSD_AES_GCM_MAC_SIZE];
} sgxsd_aes_gcm_mac_t;
_Static_assert(sizeof(sgxsd_aes_gcm_mac_t) == SGXSD_AES_GCM_MAC_SIZE, "Enclave ABI compatibility");
typedef struct sgxsd_aes_gcm_iv {
uint8_t data[SGXSD_AES_GCM_IV_SIZE];
} sgxsd_aes_gcm_iv_t;
_Static_assert(sizeof(sgxsd_aes_gcm_iv_t) == SGXSD_AES_GCM_IV_SIZE, "Enclave ABI compatibility");
typedef struct sgxsd_aes_gcm_key {
uint8_t data[SGXSD_AES_GCM_KEY_SIZE];
} sgxsd_aes_gcm_key_t;
_Static_assert(sizeof(sgxsd_aes_gcm_key_t) == SGXSD_AES_GCM_KEY_SIZE, "Enclave ABI compatibility");
typedef struct sgxsd_curve25519_public_key {
uint8_t x[SGXSD_CURVE25519_KEY_SIZE];
} sgxsd_curve25519_public_key_t;
_Static_assert(sizeof(sgxsd_curve25519_public_key_t) == SGXSD_CURVE25519_KEY_SIZE, "Enclave ABI compatibility");
typedef struct sgxsd_request_negotiation_request {
sgxsd_curve25519_public_key_t client_pubkey;
} sgxsd_request_negotiation_request_t;
_Static_assert(sizeof(sgxsd_request_negotiation_request_t) == sizeof(sgxsd_curve25519_public_key_t), "Enclave ABI compatibility");
typedef struct sgxsd_pending_request_id {
uint8_t data[sizeof(uint64_t)];
sgxsd_aes_gcm_iv_t iv;
sgxsd_aes_gcm_mac_t mac;
} sgxsd_pending_request_id_t;
_Static_assert(sizeof(sgxsd_pending_request_id_t) == sizeof(uint64_t) + sizeof(sgxsd_aes_gcm_iv_t) + sizeof(sgxsd_aes_gcm_mac_t), "Enclave ABI compatibility");
typedef struct sgxsd_request_negotiation_response {
sgxsd_curve25519_public_key_t server_static_pubkey;
sgxsd_curve25519_public_key_t server_ephemeral_pubkey;
struct {
uint8_t data[sizeof(sgxsd_pending_request_id_t)];
sgxsd_aes_gcm_iv_t iv;
sgxsd_aes_gcm_mac_t mac;
} encrypted_pending_request_id;
} sgxsd_request_negotiation_response_t;
_Static_assert(sizeof(sgxsd_request_negotiation_response_t) == sizeof(sgxsd_curve25519_public_key_t) * 2 + sizeof(sgxsd_pending_request_id_t) + sizeof(sgxsd_aes_gcm_iv_t) + sizeof(sgxsd_aes_gcm_mac_t), "Enclave ABI compatibility");
typedef struct sgxsd_msg_tag {
union {
void *p_tag;
uint64_t tag;
};
} sgxsd_msg_tag_t;
_Static_assert(sizeof(sgxsd_msg_tag_t) == sizeof(uint64_t), "Enclave ABI compatibility");
typedef struct sgxsd_msg_header {
sgxsd_aes_gcm_iv_t iv;
sgxsd_aes_gcm_mac_t mac;
sgxsd_pending_request_id_t pending_request_id;
} sgxsd_msg_header_t;
_Static_assert(sizeof(sgxsd_msg_header_t) == sizeof(sgxsd_aes_gcm_iv_t) + sizeof(sgxsd_aes_gcm_mac_t) + sizeof(sgxsd_pending_request_id_t), "Enclave ABI compatibility");
typedef struct sgxsd_node_init_args {
uint8_t pending_requests_table_order;
} sgxsd_node_init_args_t;
typedef uint64_t sgxsd_server_state_handle_t;
//
// public api (untrusted)
//
#define sgxsd_status_ok() (sgxsd_status_t) { .ok = true, .name = "ok", .code = 0 }
#define sgxsd_status_error(Name) (sgxsd_status_t) { .ok = false, .name = Name, .code = 0 }
#define sgxsd_status_error_code(Name, SgxStatus) (sgxsd_status_t) { .ok = false, .name = Name, .code = SgxStatus }
typedef struct sgxsd_status {
bool ok;
const char *name;
int64_t code;
} sgxsd_status_t;
typedef struct sgxsd_enclave {
sgx_enclave_id_t id;
union {
sgx_epid_group_id_t gid;
uint32_t gid32;
};
sgx_launch_token_t launch_token;
} sgxsd_enclave_t;
typedef sgxsd_status_t (*sgxsd_start_callback_t)(sgxsd_enclave_t, va_list);
sgxsd_status_t sgxsd_start(const char *enclave_path, bool debug, const sgx_launch_token_t *p_launch_token, const sgxsd_node_init_args_t *p_node_init_args, sgxsd_start_callback_t p_callback, ...);
sgxsd_status_t sgxsd_get_next_quote(sgx_enclave_id_t enclave_id, sgx_spid_t spid, const uint8_t *p_sig_rl, uint32_t sig_rl_size, sgx_quote_t *p_quote, uint32_t quote_size);
//
// error codes
//
typedef enum sgxsd_status_code {
SGXSD_ERROR_PENDING_REQUEST_NOT_FOUND = SGX_MK_ERROR(0x10001),
} sgxsd_status_code_t;
#endif

View File

@ -0,0 +1,58 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TEST_MEMSET_S_H__
#define TEST_MEMSET_S_H__
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
static void *(* const volatile __memset_vp)(void *, int, size_t) = memset;
static inline
int memset_s(void *s, size_t smax, int c, size_t n) {
int err = 0;
if (s == NULL) {
errno = err = EINVAL;
return err;
}
if (smax > SIZE_MAX) {
errno = err = E2BIG;
return err;
}
if (n > SIZE_MAX) {
err = E2BIG;
n = smax;
}
if (n > smax) {
err = EOVERFLOW;
n = smax;
}
(*__memset_vp)(s, c, n);
if (err == 0) {
return 0;
} else {
errno = err;
return err;
}
}
#endif

View File

@ -0,0 +1,2 @@
[build]
target-dir = "../build/target"

View File

@ -0,0 +1,44 @@
[package]
authors = ["Open Whisper Systems"]
name = "kbupd_enclave"
version = "0.1.0"
license = "AGPL-3.0-or-later"
edition = "2018"
[features]
default = []
insecure = []
debug = ["insecure"]
trace = ["debug", "insecure"]
test = ["sgx_ffi/test", "sgxsd_ffi/test"]
[dependencies]
base64 = { rev = "07b1d6b713cc2bd7d107185bd0d14bf06cddfb48", git = "https://github.com/marshallpierce/rust-base64.git", default-features = false, features = ["alloc"] }
bytes = { rev = "ebe96021b0eaf52be1fedd0a925f4384275c9cc4", git = "https://github.com/tokio-rs/bytes.git", default-features = false, features = [] }
chrono = { rev = "de22e82a1b00b8f015a1d736e497c3177e8e8c9f", git = "https://github.com/geogriff-signal/chrono.git", default-features = false, features = [] }
hashbrown = { version = "0.6", default-features = false, features = [] }
intrusive-collections = { rev = "3c14ea97598616a2beaa97cd5fbbb27ba574fe77", git = "https://github.com/geogriff-signal/intrusive-rs.git" }
lazy_static = { version = "1.4", features = ["spin_no_std"] }
libc = { version = "0.2", default-features = false, features = [] }
no-std-compat = { version = "0.2", features = ["alloc"] }
num-traits = { version = "0.2", default-features = false, features = [] }
prost = { rev = "907f7d6e714d0b449d75269d532561c71d3ed250", git = "https://github.com/geogriff-signal/prost.git", default-features = false, features = ["prost-derive"] }
rand_core = { version = "0.5", default-features = false, features = [] }
serde = { version = "1.0", default-features = false, features = ["derive", "alloc"] }
serde_json = { rev = "d79b0c67f62e168d4872bb8694377ffd97b8949f", git = "https://github.com/geogriff-signal/serde_json.git", default-features = false, features = [] }
sgx_ffi = { path = "../sgx_ffi" }
sgxsd_ffi = { path = "../sgxsd_ffi" }
spin = "0.5"
snow = { rev = "d8d00a37c8e39b2557d23a26cc4f722595b4f2d9", git = "https://github.com/geogriff-signal/snow.git", default-features = false, features = [] }
webpki = { rev = "32ab63c46edfbfe9c1fe68008ba8e247eb387ca3", git = "https://github.com/geogriff-signal/webpki.git", default-features = false, features = [] }
[dev-dependencies]
lazy_static = "1.4"
mockers = "0.21"
mockers_derive = "0.21"
rand = { version = "0.7", default-features = false, features = [] }
rand_chacha = { version = "0.2", default-features = false, features = [] }
test_ffi = { path = "../test_ffi" }
[lib]
crate-type = ["staticlib"]

View File

@ -0,0 +1 @@
leak:backtrace_alloc

View File

@ -0,0 +1,81 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use alloc::alloc::*;
use core::cmp;
use core::ptr;
#[cfg(target_arch = "x86_64")]
const MIN_ALIGN: usize = 8;
pub struct System;
unsafe impl GlobalAlloc for System {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
libc::malloc(layout.size()) as *mut u8
} else {
libc::memalign(layout.align(), layout.size()) as *mut u8
}
}
#[inline]
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() {
libc::calloc(layout.size(), 1) as *mut u8
} else {
let ptr = self.alloc(layout.clone());
if !ptr.is_null() {
ptr::write_bytes(ptr, 0, layout.size());
}
ptr
}
}
#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
libc::free(ptr as *mut libc::c_void)
}
#[inline]
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
if layout.align() <= MIN_ALIGN && layout.align() <= new_size {
libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8
} else {
realloc_fallback(self, ptr, layout, new_size)
}
}
}
unsafe fn realloc_fallback(alloc: &System, ptr: *mut u8, old_layout: Layout, new_size: usize) -> *mut u8 {
let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align());
let new_ptr = GlobalAlloc::alloc(alloc, new_layout);
if !new_ptr.is_null() {
let size = cmp::min(old_layout.size(), new_size);
ptr::copy_nonoverlapping(ptr, new_ptr, size);
GlobalAlloc::dealloc(alloc, ptr, old_layout);
}
new_ptr
}
#[alloc_error_handler]
pub fn handle_alloc_error(layout: Layout) -> ! {
let status = sgx_ffi::util::MemoryStatus::collect();
panic!("out of memory allocating {} bytes with {} used of {} bytes in {} chunks",
layout.size(), status.used_bytes, status.footprint_bytes, status.free_chunks);
}

View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "kbupd_sgxsd_callbacks.h"
#include "kbupd_enclave_t.h"

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,235 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::prelude::*;
use std::cell::*;
use std::ptr::{NonNull};
use std::slice;
use prost::Message;
use sgx_ffi::untrusted_slice::{UntrustedSlice};
pub use super::bindgen_wrapper::{
sgxsd_server_init_args_t as StartArgs,
sgxsd_server_handle_call_args_t as CallArgs,
sgxsd_server_terminate_args_t as StopArgs,
KBUPD_REQUEST_TYPE_ANY,
KBUPD_REQUEST_TYPE_BACKUP,
KBUPD_REQUEST_TYPE_RESTORE,
KBUPD_REQUEST_TYPE_DELETE,
};
use super::bindgen_wrapper::{
kbupd_enclave_ocall_recv_enclave_msg,
kbupd_enclave_ocall_alloc,
};
use crate::protobufs::kbupd::{UntrustedMessageBatch, UntrustedMessage, EnclaveMessageBatch, EnclaveMessage};
pub trait KbupdService {
fn untrusted_message(&mut self, message: UntrustedMessage);
}
const ENCLAVE_MESSAGE_BUFFER_SIZE: usize = 10240;
#[cfg(not(any(test, feature = "test")))]
pub fn with_buffer<F, R>(fun: F) -> R
where F: FnOnce(&RefCell<Option<Vec<u8>>>) -> R
{
#[thread_local]
static ENCLAVE_MESSAGE_BUFFER: RefCell<Option<Vec<u8>>> = RefCell::new(None);
fun(&ENCLAVE_MESSAGE_BUFFER)
}
#[cfg(any(test, feature = "test"))]
pub fn with_buffer<F, R>(fun: F) -> R
where F: FnOnce(&RefCell<Option<Vec<u8>>>) -> R
{
thread_local! {
static ENCLAVE_MESSAGE_BUFFER: RefCell<Option<Vec<u8>>> = RefCell::new(None);
}
ENCLAVE_MESSAGE_BUFFER.with(fun)
}
pub fn kbupd_enclave_alloc_untrusted(mut size: usize) -> Result<UntrustedSlice<'static>, ()> {
let mut p_data: *mut libc::c_void = std::ptr::null_mut();
match unsafe { kbupd_enclave_ocall_alloc(&mut p_data, &mut size) } {
0 => UntrustedSlice::new(p_data as *mut u8, size),
error => {
error!("ocall error allocating {} bytes from untrusted: {}", size, error);
Err(())
}
}
}
pub fn kbupd_enclave_recv_untrusted_msg<S>(service: &mut S, p_data: *const u8, data_size: usize)
where S: KbupdService,
{
let data = ECallSlice(NonNull::new(p_data as *mut _), data_size);
match UntrustedMessageBatch::decode(data.as_ref()) {
Ok(batch) => {
for message in batch.messages {
service.untrusted_message(message);
}
}
Err(decode_error) => {
error!("error decoding untrusted messages: {}", decode_error);
}
}
kbupd_send_flush();
}
pub fn kbupd_send(message: EnclaveMessage) {
let batch = EnclaveMessageBatch { messages: vec![message] };
let buffer_len = with_buffer(|buffer| buffer.borrow().as_ref().map(Vec::len).unwrap_or(0));
if buffer_len.saturating_add(batch.encoded_len()) > ENCLAVE_MESSAGE_BUFFER_SIZE {
kbupd_send_flush();
}
with_buffer(|buffer| {
let mut buffer_ref_mut = RefMut::map(buffer.borrow_mut(), |maybe_buffer: &mut Option<Vec<u8>>| {
maybe_buffer.get_or_insert_with(|| Vec::with_capacity(ENCLAVE_MESSAGE_BUFFER_SIZE))
});
let buffer_mut: &mut Vec<u8> = buffer_ref_mut.as_mut();
assert!(batch.encode(buffer_mut).is_ok());
});
}
pub fn kbupd_send_flush() {
let maybe_buffer = with_buffer(|buffer_tls| {
std::mem::replace(&mut *buffer_tls.borrow_mut(), Default::default())
});
let mut buffer = match maybe_buffer {
Some(buffer) => buffer,
None => return,
};
if !buffer.is_empty() {
let ocall_res = unsafe { kbupd_enclave_ocall_recv_enclave_msg(buffer.as_ptr(), buffer.len()) };
assert_eq!(ocall_res, 0);
}
buffer.truncate(0);
with_buffer(|buffer_tls| {
if buffer_tls.borrow().as_ref().map(Vec::is_empty).unwrap_or(true) {
if buffer.capacity() == ENCLAVE_MESSAGE_BUFFER_SIZE {
*buffer_tls.borrow_mut() = Some(buffer);
}
}
});
}
struct ECallSlice(Option<NonNull<u8>>, usize);
impl AsRef<[u8]> for ECallSlice {
fn as_ref(&self) -> &[u8] {
if self.1 != 0 {
if let Some(ptr) = self.0 {
unsafe { slice::from_raw_parts(ptr.as_ptr(), self.1) }
} else {
&[]
}
} else {
&[]
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::mocks;
use mockers::*;
struct MockKbupdService {}
impl KbupdService for MockKbupdService {
fn untrusted_message(&mut self, _message: UntrustedMessage) {
}
}
#[test]
fn kbupd_enclave_recv_untrusted_msg_empty() {
let mut kbupd_service = MockKbupdService {};
kbupd_enclave_recv_untrusted_msg(&mut kbupd_service, std::ptr::null(), 0);
}
#[test]
fn kbupd_enclave_recv_untrusted_msg_bad() {
let bad_requests: &[&[u8]] = &[
// bad tag 0, types 0..=7, truncated
&[0x00], &[0x01], &[0x02], &[0x03], &[0x04], &[0x05], &[0x06], &[0x07],
// tag 1, bad types 0..=1, truncated
&[0x08], &[0x09],
// tag 1, type 2, truncated
&[0x0A],
// tag 1, bad types 3..=7, truncated
&[0x0B], &[0x0C], &[0x0D], &[0x0E], &[0x0F],
// tag 2, types 0..=7, truncated
&[0x10], &[0x11], &[0x12], &[0x13], &[0x14], &[0x15], &[0x16], &[0x17],
// tag 1, bad type 0
&[0x08, 0x00],
// tag 1, type 2, length 1, truncated
&[0x0A, 0x01],
// tag 1, type 2, length 2^64-1, truncated
&[0x0A, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01],
// tag 1, type 2, length 1 (overlong varint), truncated
&[0x0A, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00],
&[0x0A, 0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x02],
// tag 2, type 0, bad varints
&[0x10, 0x80],
&[0x10, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80],
&[0x10, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00],
// bad tag 0 (overlong varint), type 0
&[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00],
&[0x80, 0x80, 0x80, 0x00, 0x00],
// bad tag 2^32, type 0
&[0x80, 0x80, 0x80, 0x80, 0x10, 0x00],
// bad tag 2^64-1, type 0
&[0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00],
// bad tag (bad varint)
&[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80],
&[0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00],
];
let null_requests: &[&[u8]] = &[
// empty
&[],
// tag 1, type 2, length 0 (overlong varint)
&[0x0A, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00],
// tag 1, type 2, length 0 (overlong varint, extra bits ignored)
&[0x0A, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x02],
// tag 1 (overlong varint), type 2, length 0
&[0x8A, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00],
// tag 2 (overlong varint), type 0
&[0x90, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x00],
// tag 2 (overlong varint, extra bits ignored), type 0
&[0x90, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x02, 0x00],
];
let requests = bad_requests.iter().chain(null_requests.iter());
for request in requests.into_iter() {
let scenario = Scenario::new();
mocks::expect_enclave_messages(&scenario, vec![]);
let mut kbupd_service = MockKbupdService {};
kbupd_enclave_recv_untrusted_msg(&mut kbupd_service, request.as_ptr(), request.len());
drop(scenario);
}
}
}

View File

@ -0,0 +1,153 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use std::cell::{RefCell};
use std;
use mockers::*;
use mockers::matchers::*;
use mockers_derive::mocked;
use prost::{Message};
use crate::protobufs;
use super::bindgen_wrapper::{
sgx_status_t,
};
//
// mock extern "C" functions
//
thread_local! {
pub static KBUPD_ENCLAVE_OCALL_RECV_ENCLAVE_MSG: RefCell<Option<KbupdEnclaveOcallRecvEnclaveMsgMock>> = RefCell::new(None);
pub static KBUPD_ENCLAVE_OCALL_ALLOC: RefCell<Option<KbupdEnclaveOcallAllocMock>> = RefCell::new(None);
}
#[mocked]
pub trait KbupdEnclaveOcallRecvEnclaveMsg {
fn enclave_message(&self, msg: protobufs::kbupd::enclave_message::Inner);
fn kbupd_enclave_ocall_recv_enclave_msg(&self) -> sgx_status_t;
}
#[mocked]
pub trait KbupdEnclaveOcallAlloc {
fn kbupd_enclave_ocall_alloc(&self, size: usize) -> Result<(*mut ::std::os::raw::c_void, usize), sgx_status_t>;
}
impl MatchArg<protobufs::kbupd::enclave_message::Inner> for Box<dyn MatchArg<protobufs::kbupd::enclave_message::Inner>> {
fn matches(&self, arg: &protobufs::kbupd::enclave_message::Inner) -> Result<(), String> { (**self).matches(arg) }
fn describe(&self) -> String { (**self).describe() }
}
pub fn expect_enclave_messages(scenario: &Scenario, matchers: impl IntoIterator<Item = Box<dyn MatchArg<protobufs::kbupd::enclave_message::Inner>>>) {
let mock = test_ffi::mock_for(&KBUPD_ENCLAVE_OCALL_RECV_ENCLAVE_MSG, &scenario);
for matcher in matchers {
scenario.expect(mock.enclave_message(matcher).and_return(()));
}
scenario.expect(mock.kbupd_enclave_ocall_recv_enclave_msg().and_return_clone(0).times(..));
}
pub fn expect_kbupd_enclave_ocall_alloc(scenario: &Scenario,
request_size: usize,
returned_ptr: *mut libc::c_void,
returned_size: usize) {
assert_ne!(request_size, 0);
let mock = test_ffi::mock_for(&KBUPD_ENCLAVE_OCALL_ALLOC, &scenario);
scenario.expect(mock.kbupd_enclave_ocall_alloc(
eq(request_size)
).and_return(Ok((returned_ptr, returned_size))));
sgx_ffi::mocks::expect_sgx_is_outside_enclave(scenario, returned_ptr as *const libc::c_void, returned_size, true);
}
//
// mock extern "C" function implementations
//
pub mod impls {
use super::*;
struct OCallSlice(*const u8, usize);
impl AsRef<[u8]> for OCallSlice {
fn as_ref(&self) -> &[u8] {
if self.1 != 0 {
assert!(!self.0.is_null());
unsafe { std::slice::from_raw_parts(self.0, self.1) }
} else {
unsafe { std::slice::from_raw_parts(1 as *const u8, 0) }
}
}
}
#[no_mangle]
pub extern "C" fn kbupd_enclave_ocall_recv_enclave_msg(data: *const u8, data_size: usize) -> sgx_status_t {
let data = OCallSlice(data, data_size);
let batch = protobufs::kbupd::EnclaveMessageBatch::decode(data.as_ref()).expect("bad EnclaveMessageBatch from enclave");
KBUPD_ENCLAVE_OCALL_RECV_ENCLAVE_MSG.with(|mock_cell| {
let mock_ref = mock_cell.borrow();
let mock = mock_ref.as_ref().expect("no mock for kbupd_enclave_ocall_recv_enclave_msg");
for msg in batch.messages {
match msg.inner.expect("empty EnclaveMessage from enclave") {
protobufs::kbupd::enclave_message::Inner::EnclaveLogSignal(log) => {
eprintln!("enclave log: {}", std::str::from_utf8(&log.message).unwrap());
}
msg_inner => {
mock.enclave_message(msg_inner);
}
}
}
mock.kbupd_enclave_ocall_recv_enclave_msg()
})
}
#[no_mangle]
pub extern "C" fn kbupd_enclave_ocall_alloc(p_ptr_out: *mut *mut ::std::os::raw::c_void, p_size_in_out: *mut usize) -> sgx_status_t {
assert!(!p_ptr_out.is_null());
assert!(!p_size_in_out.is_null());
let size = unsafe { *p_size_in_out };
assert_ne!(size, 0);
let res = KBUPD_ENCLAVE_OCALL_ALLOC.with(|mock| {
(mock.borrow().as_ref().expect("no mock for kbupd_enclave_ocall_alloc"))
.kbupd_enclave_ocall_alloc(size)
});
match res {
Ok((ptr, size)) => {
unsafe {
*p_ptr_out = ptr;
*p_size_in_out = size;
}
0
}
Err(error) => error,
}
}
#[no_mangle]
pub extern "C" fn curve25519_donna(
arg1: *mut ::std::os::raw::c_uchar,
arg2: *const ::std::os::raw::c_uchar,
arg3: *const ::std::os::raw::c_uchar,
) -> ::std::os::raw::c_int {
let arg1 = unsafe { std::slice::from_raw_parts_mut(arg1, 32) };
let arg2 = unsafe { std::slice::from_raw_parts(arg2, 32) };
let arg3 = unsafe { std::slice::from_raw_parts(arg3, 32) };
test_ffi::read_rand(arg1);
arg2.iter().for_each(|p| unsafe { std::ptr::read_volatile(p); });
arg3.iter().for_each(|p| unsafe { std::ptr::read_volatile(p); });
0
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#[allow(dead_code, non_camel_case_types, non_upper_case_globals, non_snake_case, improper_ctypes, clippy::all, clippy::pedantic, clippy::integer_arithmetic)]
mod bindgen_wrapper;
pub mod ecalls;
#[cfg(not(any(test, feature = "test")))]
mod panic;
pub mod snow_resolver;
#[cfg(test)] pub mod mocks;

View File

@ -0,0 +1,30 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use alloc::string::{ToString};
use core::panic::{PanicInfo};
use super::bindgen_wrapper::{kbupd_enclave_ocall_panic};
#[panic_handler]
fn panic(info: &PanicInfo<'_>) -> ! {
let message = info.to_string();
unsafe {
kbupd_enclave_ocall_panic(message.as_ptr(), message.len());
libc::abort()
}
}

View File

@ -0,0 +1,391 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use core::convert::{TryInto};
use crate::prelude::*;
use rand_core::{RngCore, CryptoRng};
use sgxsd_ffi::*;
use snow::resolvers::*;
use snow::types::*;
use snow::params::*;
#[derive(Default)]
pub struct SnowResolver;
#[derive(Default)]
struct SnowRdRand;
#[derive(Default)]
struct SnowDh25519 {
key: Curve25519Key,
}
#[derive(Default)]
struct SnowHashSHA256 {
context: SHA256Context,
}
#[derive(Default)]
struct SnowCipherAESGCM {
key: AesGcmKey,
}
//
// SnowResolver impls
//
impl CryptoResolver for SnowResolver {
fn resolve_rng(&self) -> Option<Box<dyn Random>> {
Some(Box::new(SnowRdRand))
}
fn resolve_dh(&self, choice: &DHChoice) -> Option<Box<dyn Dh>> {
match *choice {
DHChoice::Curve25519 => Some(Box::new(SnowDh25519::default())),
_ => None,
}
}
fn resolve_hash(&self, choice: &HashChoice) -> Option<Box<dyn Hash>> {
match *choice {
HashChoice::SHA256 => Some(Box::new(SnowHashSHA256::default())),
_ => None,
}
}
fn resolve_cipher(&self, choice: &CipherChoice) -> Option<Box<dyn Cipher>> {
match *choice {
CipherChoice::AESGCM => Some(Box::new(SnowCipherAESGCM::default())),
_ => None,
}
}
}
//
// SnowRdRand impls
//
impl Random for SnowRdRand {}
impl CryptoRng for SnowRdRand {}
impl RngCore for SnowRdRand {
fn next_u32(&mut self) -> u32 { RdRand.next_u32() }
fn next_u64(&mut self) -> u64 { RdRand.next_u64() }
fn fill_bytes(&mut self, dest: &mut [u8]) { RdRand.fill_bytes(dest) }
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> { RdRand.try_fill_bytes(dest) }
}
//
// SnowDh25519 impls
//
impl Dh for SnowDh25519 {
fn name(&self) -> &'static str { "25519" }
fn pub_len(&self) -> usize { 32 }
fn priv_len(&self) -> usize { 32 }
fn set(&mut self, privkey: &[u8]) {
let privkey: &[u8; 32] = privkey.try_into().unwrap_or_else(|_| panic!("overflow"));
self.key.set_key(privkey);
}
fn generate(&mut self, rng: &mut dyn Random) {
self.key.generate(rng);
}
fn pubkey(&self) -> &[u8] {
self.key.pubkey()
}
fn privkey(&self) -> &[u8] {
self.key.privkey()
}
fn dh(&self, pubkey: &[u8], out: &mut [u8]) -> Result<(), ()> {
let pubkey: &[u8] = pubkey.get(..32).unwrap_or_else(|| panic!("overflow"));
let pubkey: &[u8; 32] = pubkey.try_into().unwrap_or_else(|_| static_unreachable!());
let out: &mut [u8] = out.get_mut(..32).unwrap_or_else(|| panic!("overflow"));
let out: &mut [u8; 32] = out.try_into().unwrap_or_else(|_| static_unreachable!());
self.key.dh(pubkey, out);
Ok(())
}
}
//
// SnowCipherAESGCM impls
//
impl Cipher for SnowCipherAESGCM {
fn name(&self) -> &'static str { "AESGCM" }
fn set(&mut self, key: &[u8]) {
let key: &[u8; 32] = key.try_into().unwrap_or_else(|_| panic!("overflow"));
self.key.set_key(key);
}
fn encrypt(&self, nonce: u64, authtext: &[u8], plaintext: &[u8], out: &mut [u8]) -> usize {
let (text_in_out, out) = out.split_at_mut(plaintext.len());
let out: &mut [u8] = out.get_mut(..16).unwrap_or_else(|| panic!("overflow"));
let out: &mut [u8; 16] = out.try_into().unwrap_or_else(|_| static_unreachable!());
text_in_out.copy_from_slice(plaintext);
let mut mac = AesGcmMac::default();
let mut iv = AesGcmIv::default();
let iv_data: &mut [u8] = iv.data.get_mut(4..).unwrap_or_else(|| static_unreachable!());
let iv_data: &mut [u8; 8] = iv_data.try_into().unwrap_or_else(|_| static_unreachable!());
*iv_data = nonce.to_be_bytes();
match self.key.encrypt(text_in_out, authtext, &iv, &mut mac) {
Ok(()) => {
*out = mac.data;
text_in_out.len().saturating_add(mac.data.len())
}
Err(_) => {
sgx_ffi::util::clear(text_in_out);
0
}
}
}
fn decrypt(&self, nonce: u64, authtext: &[u8], ciphertext: &[u8], out: &mut [u8]) -> Result<usize, ()> {
let ciphertext_len = ciphertext.len().checked_sub(16)
.unwrap_or_else(|| panic!("overflow"));
let (ciphertext, ciphertext_mac_data) = ciphertext.split_at(ciphertext_len);
let ciphertext_mac_data: &[u8; 16] = ciphertext_mac_data.try_into().unwrap_or_else(|_| unreachable!());
let (in_out_text, _) = out.split_at_mut(ciphertext.len());
in_out_text.copy_from_slice(ciphertext);
let mac = AesGcmMac { data: *ciphertext_mac_data };
let mut iv = AesGcmIv::default();
let iv_data: &mut [u8] = iv.data.get_mut(4..).unwrap_or_else(|| static_unreachable!());
let iv_data: &mut [u8; 8] = iv_data.try_into().unwrap_or_else(|_| static_unreachable!());
*iv_data = nonce.to_be_bytes();
self.key.decrypt(in_out_text, authtext, &iv, &mac)
.map(|()| in_out_text.len())
.map_err(drop)
}
}
//
// SnowHashSHA256 impls
//
impl Hash for SnowHashSHA256 {
fn name(&self) -> &'static str { "SHA256" }
fn block_len(&self) -> usize { 64 }
fn hash_len(&self) -> usize { SHA256Context::hash_len() }
fn reset(&mut self) {
self.context.reset();
}
fn input(&mut self, data: &[u8]) {
self.context.update(data);
}
fn result(&mut self, out: &mut [u8]) {
let out: &mut [u8] = out.get_mut(..SHA256Context::hash_len()).unwrap_or_else(|| panic!("overflow"));
let out: &mut [u8; SHA256Context::hash_len()] = out.try_into().unwrap_or_else(|_| static_unreachable!());
self.context.result(out);
}
}
#[cfg(test)]
pub mod tests {
use super::*;
use mockers::*;
use mockers::matchers::*;
#[test]
fn resolve_rng_test() {
let rand = SnowResolver.resolve_rng();
assert!(rand.is_some());
if let Some(mut rand) = rand {
let mut data = vec![0; 100];
rand.fill_bytes(&mut data[..0]);
assert_eq!(&data[..], &vec![0; data.len()][..]);
rand.fill_bytes(&mut data);
assert_ne!(&data[..], &vec![0; data.len()][..]);
}
}
#[test]
fn resolve_dh_test() {
let dh = SnowResolver.resolve_dh(&DHChoice::Curve25519);
let rand = SnowResolver.resolve_rng();
assert!(dh.is_some());
assert!(rand.is_some());
if let (Some(mut dh), Some(mut rand)) = (dh, rand) {
dh.generate(&mut *rand);
let mut privkey = [0; 32];
let mut pubkey = [0; 32];
privkey.copy_from_slice(dh.privkey());
pubkey.copy_from_slice(dh.pubkey());
assert_ne!(&privkey, &[0; 32]);
assert_ne!(&pubkey, &[0; 32]);
dh.set(&test_ffi::rand_bytes([0; 32]));
assert_ne!(dh.privkey(), privkey);
assert_ne!(dh.pubkey(), pubkey);
let mut res = vec![0; 32];
dh.dh(&test_ffi::rand_bytes(vec![0; 32]), &mut res).unwrap();
assert_ne!(&res[..], &[0; 32][..]);
}
}
#[test]
#[should_panic]
fn test_dh_set_overflow() {
let dh = SnowResolver.resolve_dh(&DHChoice::Curve25519);
if let Some(mut dh) = dh {
dh.set(&[0; 31]);
}
}
#[test]
#[should_panic]
fn test_dh_pubkey_overflow() {
let dh = SnowResolver.resolve_dh(&DHChoice::Curve25519);
if let Some(dh) = dh {
let _ignore = dh.dh(&test_ffi::rand_bytes(vec![0; 31]), &mut vec![0; 32]);
}
}
#[test]
#[should_panic]
fn test_dh_out_overflow() {
let dh = SnowResolver.resolve_dh(&DHChoice::Curve25519);
if let Some(dh) = dh {
let _ignore = dh.dh(&test_ffi::rand_bytes(vec![0; 32]), &mut vec![0; 31]);
}
}
#[test]
fn resolve_hash_test() {
let hash = SnowResolver.resolve_hash(&HashChoice::SHA256);
assert!(hash.is_some());
if let Some(mut hash) = hash {
hash.reset();
hash.input(&[]);
hash.input(&[0]);
hash.input(&test_ffi::rand_bytes(vec![0; 100]));
let mut result = vec![0; hash.hash_len()];
hash.result(&mut result);
assert_ne!(&result[..], &vec![0; hash.hash_len()][..]);
}
}
macro_rules! eq_vec {
($vec:expr) => ({
let vec: Vec<u8> = $vec.clone();
check(move |slice: &&[u8]| slice == &&vec[..])
})
}
#[test]
fn resolve_cipher_test() {
let cipher = SnowResolver.resolve_cipher(&CipherChoice::AESGCM);
assert!(cipher.is_some());
if let Some(mut cipher) = cipher {
let privkey = test_ffi::rand_bytes(vec![0; 32]);
cipher.set(&privkey);
test_ffi::clear(&sgxsd_ffi::mocks::SGXSD_AES_GCM_ENCRYPT);
let authtext = vec![0; 100];
let plaintext = vec![0; 100];
let mut out = vec![0; plaintext.len() + 16];
assert_eq!(cipher.encrypt(0, &authtext, &plaintext, &mut out), out.len());
assert_ne!(&vec![0; 8][..], &out[..8]);
assert_ne!(&vec![0; 8][..], &out[(out.len() - 8)..]);
test_ffi::clear(&sgxsd_ffi::mocks::SGXSD_AES_GCM_DECRYPT);
let ciphertext = vec![0; plaintext.len() + 16];
let mut out = vec![0; plaintext.len()];
assert_eq!(cipher.decrypt(0, &authtext, &ciphertext, &mut out), Ok(out.len()));
assert_ne!(&out[..], &vec![0; out.len()][..]);
}
}
#[test]
fn resolve_cipher_success() {
let scenario = Scenario::new();
let mut cipher = SnowResolver.resolve_cipher(&CipherChoice::AESGCM).unwrap();
let privkey = test_ffi::rand_bytes(vec![0; 32]);
let authtext = test_ffi::rand_bytes(vec![0; 100]);
let plaintext = test_ffi::rand_bytes(vec![0; 100]);
let ciphertext = test_ffi::rand_bytes(vec![0; 100]);
cipher.set(&privkey);
let mock = test_ffi::mock_for(&sgxsd_ffi::mocks::SGXSD_AES_GCM_ENCRYPT, &scenario);
scenario.expect(mock.sgxsd_aes_gcm_encrypt(
eq_vec!(privkey),
eq_vec!(plaintext),
any(),
eq_vec!(authtext)
).and_return(Ok(ciphertext.clone())));
let mut ciphertext_and_tag_out = vec![0; plaintext.len() + 16];
assert_eq!(cipher.encrypt(0, &authtext, &plaintext, &mut ciphertext_and_tag_out), ciphertext_and_tag_out.len());
assert_eq!(&ciphertext_and_tag_out[..ciphertext.len()], &ciphertext[..]);
let mock = test_ffi::mock_for(&sgxsd_ffi::mocks::SGXSD_AES_GCM_DECRYPT, &scenario);
scenario.expect(mock.sgxsd_aes_gcm_decrypt(
eq_vec!(privkey),
eq_vec!(ciphertext),
any(),
eq_vec!(authtext)
).and_return(Ok(plaintext.clone())));
let mut plaintext_out = vec![0; plaintext.len()];
assert_eq!(cipher.decrypt(0, &authtext, &ciphertext_and_tag_out, &mut plaintext_out), Ok(plaintext_out.len()));
assert_eq!(&plaintext_out, &plaintext);
}
#[test]
fn resolve_cipher_fail() {
let scenario = Scenario::new();
let mut cipher = SnowResolver.resolve_cipher(&CipherChoice::AESGCM).unwrap();
let privkey = test_ffi::rand_bytes(vec![0; 32]);
let authtext = test_ffi::rand_bytes(vec![0; 100]);
let plaintext = test_ffi::rand_bytes(vec![0; 100]);
let ciphertext = test_ffi::rand_bytes(vec![0; 100]);
cipher.set(&privkey);
let mock = test_ffi::mock_for(&sgxsd_ffi::mocks::SGXSD_AES_GCM_ENCRYPT, &scenario);
scenario.expect(mock.sgxsd_aes_gcm_encrypt(
eq_vec!(privkey),
eq_vec!(plaintext),
any(),
eq_vec!(authtext)
).and_return(Err(())));
let mut ciphertext_and_tag_out = vec![0; plaintext.len() + 16];
assert_eq!(cipher.encrypt(0, &authtext, &plaintext, &mut ciphertext_and_tag_out), 0);
assert_eq!(&ciphertext_and_tag_out, &vec![0; ciphertext_and_tag_out.len()]);
let mock = test_ffi::mock_for(&sgxsd_ffi::mocks::SGXSD_AES_GCM_DECRYPT, &scenario);
scenario.expect(mock.sgxsd_aes_gcm_decrypt(
eq_vec!(privkey),
eq_vec!(ciphertext),
any(),
eq_vec!(authtext)
).and_return(Err(())));
ciphertext_and_tag_out[..ciphertext.len()].copy_from_slice(&ciphertext);
let mut plaintext_out = ciphertext.clone();
assert_eq!(cipher.decrypt(0, &authtext, &ciphertext_and_tag_out, &mut plaintext_out), Err(()));
assert_eq!(&plaintext_out, &ciphertext);
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#![allow(deprecated)]
use core::hash::{BuildHasher, SipHasher};
use rand_core::{RngCore};
use sgxsd_ffi::{RdRand};
#[derive(Clone)]
pub struct DefaultHasher(u64, u64);
impl Default for DefaultHasher {
fn default() -> Self {
Self(RdRand.next_u64(), RdRand.next_u64())
}
}
impl BuildHasher for DefaultHasher {
type Hasher = SipHasher;
fn build_hasher(&self) -> Self::Hasher {
SipHasher::new_with_keys(self.0, self.1)
}
}

View File

@ -0,0 +1,291 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
syntax = "proto2";
package protobufs.kbupd_enclave;
import "kbupd.proto";
import "kbupd_client.proto";
import "raft.proto";
message SecretBytes {
required bytes data = 1;
}
//
// transactions
//
message TransactionData {
oneof inner {
FrontendRequestTransaction frontend_request = 1;
StartXferTransaction start_xfer = 2;
SetSidTransaction set_sid = 3;
RemoveChunkTransaction remove_chunk = 4;
ApplyChunkTransaction apply_chunk = 5;
PauseXferTransaction pause_xfer = 6;
ResumeXferTransaction resume_xfer = 7;
FinishXferTransaction finish_xfer = 8;
SetTimeTransaction set_time = 9;
};
}
message FrontendRequestTransaction {
required bytes from_node_id = 1;
required uint64 request_id = 2;
oneof transaction {
CreateBackupTransaction create = 3;
BackupTransaction backup = 4;
RestoreTransaction restore = 5;
DeleteBackupTransaction delete = 6;
};
}
message CreateBackupTransaction {
required kbupd.BackupId backup_id = 1;
required bytes new_creation_nonce = 2;
required bytes new_nonce = 3;
}
message BackupTransaction {
required kbupd.BackupId backup_id = 1;
required bytes old_nonce = 2;
required bytes new_creation_nonce = 3;
required bytes new_nonce = 4;
required SecretBytes data = 5;
required SecretBytes pin = 6;
required uint32 tries = 7;
}
message RestoreTransaction {
required kbupd.BackupId backup_id = 1;
required bytes creation_nonce = 2;
required bytes old_nonce = 3;
required bytes new_nonce = 4;
required SecretBytes pin = 5;
}
message DeleteBackupTransaction {
required kbupd.BackupId backup_id = 1;
}
message StartXferTransaction {
required bytes from_node_id = 1;
required XferRequest xfer_request = 2;
}
message SetSidTransaction {
required bytes from_node_id = 1;
required kbupd.ServiceId service_id = 2;
}
message RemoveChunkTransaction {
required bytes from_node_id = 1;
required XferChunkReply xfer_chunk_reply = 2;
required kbupd.BackupId chunk_last = 3;
}
message ApplyChunkTransaction {
required bytes from_node_id = 1;
required XferChunkRequest xfer_chunk_request = 2;
required XferChunkReply xfer_chunk_reply = 3;
}
message PauseXferTransaction {
required uint64 request_id = 1;
}
message ResumeXferTransaction {
required uint64 request_id = 1;
required kbupd.BackupId chunk_last = 2;
}
message FinishXferTransaction {
required uint64 request_id = 1;
required bool force = 2;
}
message SetTimeTransaction {
required uint64 now_secs = 1;
}
//
// remote enclave handshake
//
enum NodeType {
NODE_TYPE_NONE = 0;
NODE_TYPE_FRONTEND = 1;
NODE_TYPE_REPLICA = 2;
}
message PeerConnectRequest {
required NodeType node_type = 1;
optional kbupd.IasReport ias_report = 2;
required bytes noise_data = 3;
}
message PeerConnectReply {
required bytes sgx_quote = 1;
required bytes noise_data = 2;
}
//
// enclave-to-enclave requests
//
message EnclaveGetQuoteRequest {
}
message EnclaveGetQuoteReply {
required bytes sgx_quote = 1;
}
//
// frontend to replica
//
message FrontendToReplicaMessage {
oneof inner {
TransactionRequest transaction_request = 1;
EnclaveGetQuoteRequest enclave_get_quote_request = 2;
};
}
message TransactionRequest {
required uint64 request_id = 1;
oneof data {
kbupd.CreateBackupRequest create = 2;
BackupTransactionRequest backup = 3;
RestoreTransactionRequest restore = 4;
DeleteTransactionRequest delete = 5;
};
}
message BackupTransactionRequest {
optional bytes service_id = 1;
required kbupd.BackupId backup_id = 2;
required bytes nonce = 3;
required uint64 valid_from = 4;
required SecretBytes data = 5;
required SecretBytes pin = 6;
required uint32 tries = 7;
}
message RestoreTransactionRequest {
optional bytes service_id = 1;
required kbupd.BackupId backup_id = 2;
required bytes nonce = 3;
required uint64 valid_from = 4;
required SecretBytes pin = 5;
}
message DeleteTransactionRequest {
optional bytes service_id = 1;
required kbupd.BackupId backup_id = 2;
}
//
// replica to frontend
//
message ReplicaToFrontendMessage {
oneof inner {
TransactionReply transaction_reply = 1;
EnclaveGetQuoteReply enclave_get_quote_reply = 2;
};
}
message TransactionReply {
required uint64 request_id = 1;
oneof data {
kbupd_client.Response client_response = 2;
kbupd.CreateBackupReply create_backup_reply = 3;
kbupd.DeleteBackupReply delete_backup_reply = 4;
TransactionErrorNotLeader not_leader = 5;
TransactionErrorWrongPartition wrong_partition = 6;
TransactionErrorServiceIdMismatch service_id_mismatch = 7;
TransactionErrorXferInProgress xfer_in_progress = 8;
TransactionErrorInvalidRequest invalid_request = 9;
TransactionErrorInternalError internal_error = 10;
};
}
message TransactionErrorNotLeader {
optional bytes leader_node_id = 1;
required raft.TermId term = 2;
}
message TransactionErrorWrongPartition {
optional kbupd.PartitionKeyRangePB range = 1;
optional kbupd.PartitionConfig new_partition = 2;
}
message TransactionErrorServiceIdMismatch {
}
message TransactionErrorXferInProgress {
}
message TransactionErrorInvalidRequest {
}
message TransactionErrorInternalError {
}
//
// replica to replica
//
message ReplicaToReplicaMessage {
oneof inner {
raft.RaftMessage raft_message = 1;
CreateRaftGroupRequest create_raft_group_request = 2;
EnclaveGetQuoteRequest enclave_get_quote_request = 8;
EnclaveGetQuoteReply enclave_get_quote_reply = 9;
XferRequest xfer_request = 3;
XferReply xfer_reply = 4;
XferChunkRequest xfer_chunk_request = 5;
XferChunkReply xfer_chunk_reply = 6;
XferErrorNotLeader xfer_error_not_leader = 7;
};
}
message CreateRaftGroupRequest {
optional kbupd.ServiceId service_id = 1;
required raft.RaftGroupId group_id = 2;
repeated bytes node_ids = 3;
required kbupd.EnclaveReplicaGroupConfig config = 4;
optional kbupd.SourcePartitionConfig source_partition = 5;
}
message XferRequest {
required uint32 chunk_size = 1;
required kbupd.PartitionKeyRangePB full_range = 2;
repeated bytes node_ids = 3;
required raft.RaftGroupId group_id = 4;
}
message XferReply {
required kbupd.ServiceId service = 1;
}
message XferChunkRequest {
required SecretBytes data = 1;
required kbupd.PartitionKeyRangePB chunk_range = 2;
required kbupd.AttestationParameters min_attestation = 3;
}
message XferChunkReply {
required kbupd.BackupId new_last = 1;
required uint32 chunk_size = 2;
}
message XferErrorNotLeader {
optional bytes leader_node_id = 1;
required raft.TermId term = 2;
}

View File

@ -0,0 +1,137 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#![crate_type = "staticlib"]
#![cfg_attr(not(any(test, feature = "test")), no_std)]
#![cfg_attr(not(any(test, feature = "test")), feature(alloc_error_handler))]
#![cfg_attr(not(any(test, feature = "test")), feature(thread_local))]
#![allow(
unused_parens,
clippy::style,
clippy::large_enum_variant,
)]
#![warn(
bare_trait_objects,
elided_lifetimes_in_paths,
trivial_numeric_casts,
variant_size_differences,
clippy::integer_arithmetic,
clippy::wildcard_enum_match_arm,
)]
#![deny(
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_precision_loss,
clippy::cast_sign_loss,
clippy::clone_on_ref_ptr,
clippy::expl_impl_clone_on_copy,
clippy::explicit_into_iter_loop,
clippy::explicit_iter_loop,
clippy::float_arithmetic,
clippy::float_cmp_const,
clippy::indexing_slicing,
clippy::maybe_infinite_iter,
clippy::mem_forget,
clippy::mut_mut,
clippy::needless_borrow,
clippy::option_unwrap_used,
clippy::panicking_unwrap,
clippy::print_stdout,
clippy::redundant_clone,
clippy::replace_consts,
clippy::result_unwrap_used,
clippy::shadow_unrelated,
clippy::unimplemented,
clippy::use_debug,
clippy::use_self,
clippy::use_underscore_binding,
)]
extern crate alloc;
#[cfg(not(any(test, feature = "test")))]
extern crate no_std_compat as std;
#[cfg(not(any(test, feature = "test")))]
#[global_allocator]
static ALLOCATOR: allocator::System = allocator::System;
#[macro_use]
mod macros;
#[cfg(not(any(test, feature = "test")))]
mod allocator;
mod ffi;
mod hasher;
mod logging;
mod lru;
mod prelude;
#[allow(clippy::all, clippy::pedantic, clippy::integer_arithmetic)]
mod protobufs;
mod protobufs_impl;
mod service;
mod storage;
mod raft;
mod remote;
mod remote_group;
mod util;
pub use crate::ffi::ecalls::{
kbupd_send,
kbupd_send_flush,
};
pub mod external {
use sgx_ffi::sgx::{SgxStatus};
use sgxsd_ffi::ecalls::{SgxsdServer};
use crate::service::main;
#[no_mangle]
pub extern "C" fn sgxsd_enclave_server_init(p_args: *const <main::SgxsdState as SgxsdServer>::InitArgs,
pp_state: *mut *mut main::SgxsdState)
-> SgxStatus
{
sgxsd_ffi::ecalls::sgxsd_enclave_server_init(p_args, pp_state)
}
#[no_mangle]
pub extern "C" fn sgxsd_enclave_server_handle_call(p_args: *const <main::SgxsdState as SgxsdServer>::HandleCallArgs,
msg_buf: sgxsd_ffi::ecalls::sgxsd_msg_buf_t,
mut from: sgxsd_ffi::ecalls::sgxsd_msg_from_t,
pp_state: *mut *mut main::SgxsdState)
-> SgxStatus
{
sgxsd_ffi::ecalls::sgxsd_enclave_server_handle_call(p_args, msg_buf, &mut from, pp_state)
}
#[no_mangle]
pub extern "C" fn sgxsd_enclave_server_terminate(p_args: *const <main::SgxsdState as SgxsdServer>::TerminateArgs,
p_state: *mut main::SgxsdState)
-> SgxStatus
{
sgxsd_ffi::ecalls::sgxsd_enclave_server_terminate(p_args, p_state)
}
#[no_mangle]
pub extern "C" fn kbupd_enclave_recv_untrusted_msg(p_data: *const u8, data_size: usize) {
crate::service::main::whereis(|service_ref| {
let mut service = service_ref.borrow_mut();
crate::ffi::ecalls::kbupd_enclave_recv_untrusted_msg(&mut *service, p_data, data_size)
});
}
}

View File

@ -0,0 +1,28 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use std::sync::atomic::{AtomicBool, Ordering};
static VERBOSE: AtomicBool = AtomicBool::new(false);
pub fn verbose_logging_enabled() -> bool {
VERBOSE.load(Ordering::Relaxed)
}
pub fn set_verbose_logging_enabled(enabled: bool) {
VERBOSE.store(enabled, Ordering::SeqCst)
}

View File

@ -0,0 +1,120 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use intrusive_collections::*;
use intrusive_collections::{LinkedList};
use std::rc::*;
pub struct Lru<T> {
list: LinkedList<LruAdapter<T>>,
token: Rc<LruToken>,
length: usize,
}
pub struct LruEntry<T> {
item: T,
token: Weak<LruToken>,
link: LinkedListLink,
}
intrusive_adapter!(pub LruAdapter<T> = Rc<LruEntry<T>>: LruEntry<T> { link: LinkedListLink });
struct LruToken;
impl<T> Lru<T> {
pub fn new() -> Self {
Self {
list: LinkedList::new(LruAdapter::new()),
token: Rc::new(LruToken),
length: Default::default(),
}
}
pub fn len(&self) -> usize {
self.length
}
pub fn push_back(&mut self, item: T) -> Weak<LruEntry<T>> {
let lru_entry = Rc::new(LruEntry {
item,
token: Rc::downgrade(&self.token),
link: Default::default()
});
let lru_entry_weak = Rc::downgrade(&lru_entry);
self.list.push_back(lru_entry);
self.length = self.length.saturating_add(1);
lru_entry_weak
}
pub fn bump(&mut self, lru_entry_weak: &Weak<LruEntry<T>>) -> bool {
if let Some(lru_entry) = lru_entry_weak.upgrade() {
// check that lru_entry is a member of self.list
if let Some(lru_entry_token) = lru_entry.token.upgrade() {
if !Rc::ptr_eq(&lru_entry_token, &self.token) {
return false;
}
} else {
return false;
}
if !lru_entry.link.is_linked() {
return false;
}
// safety: lru_entry must be a member of self.list
let mut lru_cursor = unsafe {
self.list.cursor_mut_from_ptr(lru_entry.as_ref())
};
if let Some(lru_entry_rc) = lru_cursor.remove() {
self.list.push_back(lru_entry_rc);
} else {
self.list.push_back(lru_entry);
}
true
} else {
false
}
}
pub fn pop_front(&mut self) -> Option<Rc<LruEntry<T>>> {
if let Some(lru_entry) = self.list.pop_front() {
self.length = self.length.saturating_sub(1);
Some(lru_entry)
} else {
None
}
}
}
impl<T> Default for Lru<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> LruEntry<T> {
pub fn get(&self) -> &T {
&self.item
}
}
impl<'a, T> IntoIterator for &'a Lru<T> {
type Item = &'a LruEntry<T>;
type IntoIter = linked_list::Iter<'a, LruAdapter<T>>;
fn into_iter(self) -> linked_list::Iter<'a, LruAdapter<T>> {
self.list.iter()
}
}

View File

@ -0,0 +1,101 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
macro_rules! error {
($($arg:tt)*) => (log!($crate::protobufs::kbupd::EnclaveLogLevel::Error, $($arg)*));
}
macro_rules! warn {
($($arg:tt)*) => (log!($crate::protobufs::kbupd::EnclaveLogLevel::Warn, $($arg)*));
}
macro_rules! info {
($($arg:tt)*) => (log!($crate::protobufs::kbupd::EnclaveLogLevel::Info, $($arg)*));
}
macro_rules! verbose {
($($arg:tt)*) => (
if $crate::logging::verbose_logging_enabled() {
log!($crate::protobufs::kbupd::EnclaveLogLevel::Info, $($arg)*)
} else {
#[cfg(feature = "debug")]
log!($crate::protobufs::kbupd::EnclaveLogLevel::Info, $($arg)*)
}
);
}
macro_rules! debug {
($($arg:tt)*) => ({
#[cfg(feature = "debug")]
#[cfg(feature = "insecure")]
log!($crate::protobufs::kbupd::EnclaveLogLevel::Debug, $($arg)*)
});
}
macro_rules! log {
($level:expr, $msg:expr) => ({
log!($level, "{}", $msg)
});
($level:expr, $msg:expr,) => ({
log!($level, $msg)
});
($level:expr, $fmt:expr, $($arg:tt)*) => ({
$crate::kbupd_send($crate::protobufs::kbupd::EnclaveMessage {
inner: Some($crate::protobufs::kbupd::enclave_message::Inner::EnclaveLogSignal($crate::protobufs::kbupd::EnclaveLogSignal {
message: ::alloc::format!($fmt, $($arg)*).into_bytes(),
module: module_path!().as_bytes().to_vec(),
file: file!().rsplit("/").next().unwrap_or_default().as_bytes().to_vec(),
line: line!(),
level: $level.into(),
})),
});
});
}
macro_rules! assert_true {
($($arg:tt)*) => ({
assert!($($arg)+);
true
});
}
macro_rules! assert_match {
($pat:pat = $expr:expr) => ({
if let $pat = $expr {
true
} else {
panic!("assertion failed: `$pat = $expr`")
}
});
($pat:pat = $expr:expr, $($arg:tt)*) => ({
if let $pat = $expr {
true
} else {
panic!("assertion failed: `$pat = $expr`: {}", format_args!($($arg)+))
}
});
}
macro_rules! static_unreachable {
() => ({
#[cfg(not(debug_assertions))]
{
extern "C" {
pub fn __static_unreachable() -> !;
}
unsafe { __static_unreachable() };
}
#[cfg(debug_assertions)]
unreachable!()
})
}

View File

@ -0,0 +1,22 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
pub use alloc::{format, vec};
pub use alloc::borrow::{ToOwned};
pub use alloc::boxed::{Box};
pub use alloc::string::{String, ToString};
pub use alloc::vec::{Vec};

View File

@ -0,0 +1,25 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
syntax = "proto2";
package protobufs;
import "kbupd.proto";
import "kbupd_client.proto";
import "raft.proto";
import "kbupd_enclave.proto";

View File

@ -0,0 +1,722 @@
//
// shared types
//
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ServiceId {
#[prost(bytes, required, tag="1")]
pub id: std::vec::Vec<u8>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct BackupId {
#[prost(bytes, required, tag="1")]
pub id: std::vec::Vec<u8>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct PartitionKeyRangePb {
#[prost(message, required, tag="1")]
pub first: BackupId,
#[prost(message, required, tag="2")]
pub last: BackupId,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct IasReport {
#[prost(bytes, required, tag="2")]
pub body: std::vec::Vec<u8>,
#[prost(bytes, required, tag="3")]
pub signature: std::vec::Vec<u8>,
#[prost(bytes, repeated, tag="4")]
pub certificates: ::std::vec::Vec<std::vec::Vec<u8>>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct AttestationParameters {
#[prost(uint64, required, tag="1")]
pub unix_timestamp_seconds: u64,
}
//
// transaction requests
//
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct CreateBackupRequest {
#[prost(message, required, tag="1")]
pub backup_id: BackupId,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct CreateBackupReply {
#[prost(bytes, required, tag="2")]
pub nonce: std::vec::Vec<u8>,
#[prost(uint32, optional, tag="3")]
pub tries: ::std::option::Option<u32>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct DeleteBackupRequest {
#[prost(message, required, tag="1")]
pub backup_id: BackupId,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct DeleteBackupReply {
}
//
// untrusted messages
//
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct UntrustedMessageBatch {
#[prost(message, repeated, tag="1")]
pub messages: ::std::vec::Vec<UntrustedMessage>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct UntrustedMessage {
#[prost(oneof="untrusted_message::Inner", tags="1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16")]
pub inner: ::std::option::Option<untrusted_message::Inner>,
}
pub mod untrusted_message {
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Inner {
#[prost(message, tag="1")]
StartFrontendRequest(super::StartFrontendRequest),
#[prost(message, tag="2")]
StartReplicaRequest(super::StartReplicaRequest),
#[prost(message, tag="3")]
StartReplicaGroupRequest(super::StartReplicaGroupRequest),
#[prost(message, tag="4")]
UntrustedTransactionRequest(super::UntrustedTransactionRequest),
#[prost(message, tag="5")]
UntrustedXferRequest(super::UntrustedXferRequest),
#[prost(message, tag="6")]
GetEnclaveStatusRequest(super::GetEnclaveStatusRequest),
#[prost(message, tag="8")]
GetQeInfoReply(super::GetQeInfoReply),
#[prost(message, tag="9")]
GetQuoteReply(super::GetQuoteReply),
#[prost(message, tag="10")]
GetAttestationReply(super::GetAttestationReply),
#[prost(message, tag="11")]
NewMessageSignal(super::NewMessageSignal),
#[prost(message, tag="12")]
TimerTickSignal(super::TimerTickSignal),
#[prost(message, tag="13")]
SetFrontendConfigSignal(super::SetFrontendConfigSignal),
#[prost(message, tag="14")]
SetReplicaConfigSignal(super::SetReplicaConfigSignal),
#[prost(message, tag="15")]
ResetPeerSignal(super::ResetPeerSignal),
#[prost(message, tag="16")]
SetVerboseLoggingSignal(super::SetVerboseLoggingSignal),
}
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct PartitionConfig {
#[prost(bytes, required, tag="1")]
pub group_id: std::vec::Vec<u8>,
#[prost(message, optional, tag="2")]
pub range: ::std::option::Option<PartitionKeyRangePb>,
#[prost(bytes, repeated, tag="3")]
pub node_ids: ::std::vec::Vec<std::vec::Vec<u8>>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct StartFrontendRequest {
#[prost(message, repeated, tag="1")]
pub partitions: ::std::vec::Vec<PartitionConfig>,
#[prost(message, required, tag="2")]
pub config: EnclaveFrontendConfig,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveFrontendConfig {
#[prost(uint32, required, tag="1")]
pub replica_timeout_ticks: u32,
#[prost(uint32, required, tag="2")]
pub request_quote_ticks: u32,
#[prost(uint32, required, tag="3")]
pub min_connect_timeout_ticks: u32,
#[prost(uint32, required, tag="4")]
pub max_connect_timeout_ticks: u32,
#[prost(uint32, required, tag="5")]
pub pending_request_count: u32,
#[prost(uint32, required, tag="6")]
pub pending_request_ttl: u32,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SourcePartitionConfig {
#[prost(message, required, tag="1")]
pub range: PartitionKeyRangePb,
#[prost(bytes, repeated, tag="2")]
pub node_ids: ::std::vec::Vec<std::vec::Vec<u8>>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct StartReplicaRequest {
#[prost(message, required, tag="1")]
pub config: EnclaveReplicaConfig,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveReplicaConfig {
#[prost(uint32, required, tag="1")]
pub election_timeout_ticks: u32,
#[prost(uint32, required, tag="2")]
pub heartbeat_timeout_ticks: u32,
#[prost(uint32, required, tag="3")]
pub request_quote_ticks: u32,
#[prost(uint32, required, tag="4")]
pub min_connect_timeout_ticks: u32,
#[prost(uint32, required, tag="5")]
pub max_connect_timeout_ticks: u32,
#[prost(uint32, required, tag="6")]
pub attestation_expiry_ticks: u32,
#[prost(uint32, required, tag="7")]
pub replication_chunk_size: u32,
#[prost(uint32, required, tag="8")]
pub transfer_chunk_size: u32,
#[prost(uint32, required, tag="10")]
pub storage_page_cache_size: u32,
#[prost(uint32, required, tag="13")]
pub raft_log_index_page_cache_size: u32,
#[prost(uint32, required, tag="14")]
pub max_frontend_count: u32,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct StartReplicaGroupRequest {
#[prost(bytes, repeated, tag="1")]
pub peer_node_ids: ::std::vec::Vec<std::vec::Vec<u8>>,
#[prost(message, required, tag="2")]
pub config: EnclaveReplicaGroupConfig,
#[prost(message, optional, tag="3")]
pub source_partition: ::std::option::Option<SourcePartitionConfig>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveReplicaGroupConfig {
#[prost(uint32, required, tag="1")]
pub storage_size: u32,
#[prost(uint64, required, tag="2")]
pub raft_log_data_size: u64,
#[prost(uint32, required, tag="3")]
pub raft_log_index_size: u32,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct NewMessageSignal {
#[prost(bytes, required, tag="1")]
pub node_id: std::vec::Vec<u8>,
#[prost(bytes, required, tag="2")]
pub data: std::vec::Vec<u8>,
#[prost(bool, required, tag="3")]
pub syn: bool,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct TimerTickSignal {
#[prost(fixed64, required, tag="1")]
pub now_secs: u64,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SetFrontendConfigSignal {
#[prost(message, required, tag="1")]
pub config: EnclaveFrontendConfig,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SetReplicaConfigSignal {
#[prost(message, required, tag="1")]
pub config: EnclaveReplicaConfig,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ResetPeerSignal {
#[prost(bytes, required, tag="1")]
pub peer_node_id: std::vec::Vec<u8>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SetVerboseLoggingSignal {
#[prost(bool, required, tag="1")]
pub verbose_logging: bool,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct GetQeInfoReply {
#[prost(bytes, required, tag="1")]
pub mrenclave: std::vec::Vec<u8>,
#[prost(uint64, required, tag="2")]
pub flags: u64,
#[prost(uint64, required, tag="3")]
pub xfrm: u64,
#[prost(uint32, required, tag="4")]
pub misc_select: u32,
#[prost(uint32, required, tag="5")]
pub config_svn: u32,
#[prost(bytes, required, tag="6")]
pub config_id: std::vec::Vec<u8>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct GetQuoteReply {
#[prost(bytes, required, tag="1")]
pub request_id: std::vec::Vec<u8>,
#[prost(bytes, required, tag="2")]
pub sgx_quote: std::vec::Vec<u8>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct GetAttestationReply {
#[prost(bytes, required, tag="1")]
pub request_id: std::vec::Vec<u8>,
#[prost(message, required, tag="2")]
pub ias_report: IasReport,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct UntrustedTransactionRequest {
#[prost(uint64, required, tag="1")]
pub request_id: u64,
#[prost(oneof="untrusted_transaction_request::Data", tags="2, 3")]
pub data: ::std::option::Option<untrusted_transaction_request::Data>,
}
pub mod untrusted_transaction_request {
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Data {
#[prost(message, tag="2")]
CreateBackupRequest(super::CreateBackupRequest),
#[prost(message, tag="3")]
DeleteBackupRequest(super::DeleteBackupRequest),
}
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct UntrustedXferRequest {
#[prost(uint64, required, tag="1")]
pub request_id: u64,
#[prost(oneof="untrusted_xfer_request::Data", tags="2")]
pub data: ::std::option::Option<untrusted_xfer_request::Data>,
}
pub mod untrusted_xfer_request {
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Data {
#[prost(enumeration="super::XferControlCommand", tag="2")]
XferControlCommand(i32),
}
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct GetEnclaveStatusRequest {
#[prost(bool, required, tag="1")]
pub memory_status: bool,
}
//
// enclave messages
//
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveMessageBatch {
#[prost(message, repeated, tag="1")]
pub messages: ::std::vec::Vec<EnclaveMessage>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveMessage {
#[prost(oneof="enclave_message::Inner", tags="1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12")]
pub inner: ::std::option::Option<enclave_message::Inner>,
}
pub mod enclave_message {
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Inner {
#[prost(message, tag="1")]
StartFrontendReply(super::StartFrontendReply),
#[prost(message, tag="2")]
StartReplicaReply(super::StartReplicaReply),
#[prost(message, tag="3")]
StartReplicaGroupReply(super::StartReplicaGroupReply),
#[prost(message, tag="4")]
UntrustedTransactionReply(super::UntrustedTransactionReply),
#[prost(message, tag="5")]
UntrustedXferReply(super::UntrustedXferReply),
#[prost(message, tag="6")]
GetEnclaveStatusReply(super::GetEnclaveStatusReply),
#[prost(message, tag="7")]
SendMessageRequest(super::SendMessageRequest),
#[prost(message, tag="8")]
GetQeInfoRequest(super::GetQeInfoRequest),
#[prost(message, tag="9")]
GetQuoteRequest(super::GetQuoteRequest),
#[prost(message, tag="10")]
GetAttestationRequest(super::GetAttestationRequest),
#[prost(message, tag="11")]
EnclaveLogSignal(super::EnclaveLogSignal),
#[prost(message, tag="12")]
EnclaveTransactionSignal(super::EnclaveTransactionSignal),
}
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct StartFrontendReply {
#[prost(bytes, required, tag="1")]
pub node_id: std::vec::Vec<u8>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct StartReplicaReply {
#[prost(bytes, required, tag="1")]
pub node_id: std::vec::Vec<u8>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct StartReplicaGroupReply {
#[prost(message, optional, tag="1")]
pub service_id: ::std::option::Option<ServiceId>,
#[prost(bytes, optional, tag="2")]
pub group_id: ::std::option::Option<std::vec::Vec<u8>>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct GetEnclaveStatusReply {
#[prost(oneof="get_enclave_status_reply::Inner", tags="1, 2")]
pub inner: ::std::option::Option<get_enclave_status_reply::Inner>,
}
pub mod get_enclave_status_reply {
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Inner {
#[prost(message, tag="1")]
ReplicaStatus(super::EnclaveReplicaStatus),
#[prost(message, tag="2")]
FrontendStatus(super::EnclaveFrontendStatus),
}
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SendMessageRequest {
#[prost(bytes, required, tag="1")]
pub node_id: std::vec::Vec<u8>,
#[prost(bytes, required, tag="2")]
pub data: std::vec::Vec<u8>,
#[prost(bool, required, tag="3")]
pub syn: bool,
#[prost(bytes, optional, tag="4")]
pub debug_msg: ::std::option::Option<std::vec::Vec<u8>>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct GetQeInfoRequest {
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct GetQuoteRequest {
#[prost(bytes, required, tag="1")]
pub request_id: std::vec::Vec<u8>,
#[prost(bytes, required, tag="2")]
pub sgx_report: std::vec::Vec<u8>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct GetAttestationRequest {
#[prost(bytes, required, tag="1")]
pub request_id: std::vec::Vec<u8>,
#[prost(bytes, required, tag="2")]
pub sgx_quote: std::vec::Vec<u8>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct UntrustedTransactionReply {
#[prost(uint64, required, tag="1")]
pub request_id: u64,
#[prost(oneof="untrusted_transaction_reply::Data", tags="2, 3")]
pub data: ::std::option::Option<untrusted_transaction_reply::Data>,
}
pub mod untrusted_transaction_reply {
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Data {
#[prost(message, tag="2")]
CreateBackupReply(super::CreateBackupReply),
#[prost(message, tag="3")]
DeleteBackupReply(super::DeleteBackupReply),
}
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct UntrustedXferReply {
#[prost(uint64, required, tag="1")]
pub request_id: u64,
#[prost(enumeration="UntrustedXferReplyStatus", required, tag="2")]
pub status: i32,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveLogSignal {
#[prost(bytes, required, tag="1")]
pub message: std::vec::Vec<u8>,
#[prost(bytes, required, tag="2")]
pub module: std::vec::Vec<u8>,
#[prost(bytes, required, tag="3")]
pub file: std::vec::Vec<u8>,
#[prost(uint32, required, tag="4")]
pub line: u32,
#[prost(enumeration="EnclaveLogLevel", required, tag="5")]
pub level: i32,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveTransactionSignal {
#[prost(uint64, required, tag="1")]
pub log_index: u64,
#[prost(oneof="enclave_transaction_signal::Transaction", tags="2, 3, 4, 5, 6, 7, 8, 9, 10")]
pub transaction: ::std::option::Option<enclave_transaction_signal::Transaction>,
}
pub mod enclave_transaction_signal {
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Transaction {
#[prost(message, tag="2")]
FrontendRequest(super::EnclaveFrontendRequestTransaction),
#[prost(message, tag="3")]
StartXfer(super::EnclaveStartXferTransaction),
#[prost(message, tag="4")]
SetSid(super::EnclaveSetSidTransaction),
#[prost(message, tag="5")]
RemoveChunk(super::EnclaveRemoveChunkTransaction),
#[prost(message, tag="6")]
ApplyChunk(super::EnclaveApplyChunkTransaction),
#[prost(message, tag="7")]
PauseXfer(super::EnclavePauseXferTransaction),
#[prost(message, tag="8")]
ResumeXfer(super::EnclaveResumeXferTransaction),
#[prost(message, tag="9")]
FinishXfer(super::EnclaveFinishXferTransaction),
#[prost(message, tag="10")]
SetTime(super::EnclaveSetTimeTransaction),
}
}
//
// enclave transactions
//
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveFrontendRequestTransaction {
#[prost(oneof="enclave_frontend_request_transaction::Transaction", tags="1, 2, 3, 4, 5, 6, 7, 8")]
pub transaction: ::std::option::Option<enclave_frontend_request_transaction::Transaction>,
}
pub mod enclave_frontend_request_transaction {
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Transaction {
#[prost(message, tag="1")]
Create(super::EnclaveCreateBackupTransaction),
#[prost(message, tag="2")]
Backup(super::EnclaveBackupTransaction),
#[prost(message, tag="3")]
Restore(super::EnclaveRestoreTransaction),
#[prost(message, tag="4")]
Delete(super::EnclaveDeleteBackupTransaction),
#[prost(message, tag="5")]
XferInProgress(super::EnclaveTransactionErrorXferInProgress),
#[prost(message, tag="6")]
WrongPartition(super::EnclaveTransactionErrorWrongPartition),
#[prost(message, tag="7")]
InvalidRequest(super::EnclaveTransactionErrorInvalidRequest),
#[prost(message, tag="8")]
InternalError(super::EnclaveTransactionErrorInternalError),
}
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveCreateBackupTransaction {
#[prost(message, required, tag="1")]
pub backup_id: BackupId,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveBackupTransaction {
#[prost(message, required, tag="1")]
pub backup_id: BackupId,
#[prost(enumeration="super::kbupd_client::backup_response::Status", required, tag="2")]
pub status: i32,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveRestoreTransaction {
#[prost(message, required, tag="1")]
pub backup_id: BackupId,
#[prost(enumeration="super::kbupd_client::restore_response::Status", required, tag="2")]
pub status: i32,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveDeleteBackupTransaction {
#[prost(message, required, tag="1")]
pub backup_id: BackupId,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveTransactionErrorXferInProgress {
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveTransactionErrorWrongPartition {
#[prost(bool, required, tag="1")]
pub new_partition_unknown: bool,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveTransactionErrorInvalidRequest {
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveTransactionErrorInternalError {
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveStartXferTransaction {
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveSetSidTransaction {
#[prost(message, optional, tag="1")]
pub service_id: ::std::option::Option<ServiceId>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveRemoveChunkTransaction {
#[prost(message, optional, tag="1")]
pub chunk_range: ::std::option::Option<PartitionKeyRangePb>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveApplyChunkTransaction {
#[prost(message, optional, tag="1")]
pub chunk_range: ::std::option::Option<PartitionKeyRangePb>,
#[prost(message, repeated, tag="2")]
pub chunk_ids: ::std::vec::Vec<BackupId>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclavePauseXferTransaction {
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveResumeXferTransaction {
#[prost(message, optional, tag="1")]
pub chunk_range: ::std::option::Option<PartitionKeyRangePb>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveFinishXferTransaction {
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveSetTimeTransaction {
#[prost(uint64, optional, tag="1")]
pub now_secs: ::std::option::Option<u64>,
}
//
// enclave status
//
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveMemoryStatus {
#[prost(uint32, required, tag="1")]
pub footprint_bytes: u32,
#[prost(uint32, required, tag="2")]
pub used_bytes: u32,
#[prost(uint32, required, tag="3")]
pub free_chunks: u32,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveReplicaStatus {
#[prost(message, optional, tag="1")]
pub memory_status: ::std::option::Option<EnclaveMemoryStatus>,
#[prost(message, optional, tag="2")]
pub partition: ::std::option::Option<EnclaveReplicaPartitionStatus>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveReplicaPartitionStatus {
#[prost(bytes, required, tag="1")]
pub group_id: std::vec::Vec<u8>,
#[prost(bytes, optional, tag="2")]
pub service_id: ::std::option::Option<std::vec::Vec<u8>>,
#[prost(message, optional, tag="3")]
pub range: ::std::option::Option<PartitionKeyRangePb>,
#[prost(message, repeated, tag="4")]
pub peers: ::std::vec::Vec<EnclavePeerStatus>,
#[prost(message, required, tag="5")]
pub min_attestation: AttestationParameters,
#[prost(bool, required, tag="6")]
pub is_leader: bool,
#[prost(uint64, required, tag="7")]
pub current_term: u64,
#[prost(uint64, required, tag="8")]
pub prev_log_index: u64,
#[prost(uint64, required, tag="9")]
pub last_applied_index: u64,
#[prost(uint64, required, tag="10")]
pub commit_index: u64,
#[prost(uint64, required, tag="11")]
pub last_log_index: u64,
#[prost(uint64, required, tag="12")]
pub last_log_term: u64,
#[prost(uint64, required, tag="13")]
pub log_data_length: u64,
#[prost(uint64, required, tag="14")]
pub backup_count: u64,
#[prost(oneof="enclave_replica_partition_status::XferStatus", tags="15, 16")]
pub xfer_status: ::std::option::Option<enclave_replica_partition_status::XferStatus>,
}
pub mod enclave_replica_partition_status {
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum XferStatus {
#[prost(message, tag="15")]
IncomingXferStatus(super::EnclaveIncomingXferStatus),
#[prost(message, tag="16")]
OutgoingXferStatus(super::EnclaveOutgoingXferStatus),
}
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclavePeerStatus {
#[prost(bytes, required, tag="1")]
pub node_id: std::vec::Vec<u8>,
#[prost(message, optional, tag="2")]
pub attestation: ::std::option::Option<AttestationParameters>,
#[prost(message, optional, tag="3")]
pub replication_status: ::std::option::Option<EnclavePeerReplicationStatus>,
#[prost(bool, required, tag="4")]
pub is_leader: bool,
#[prost(uint64, required, tag="5")]
pub inflight_requests: u64,
#[prost(uint64, required, tag="6")]
pub unsent_requests: u64,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclavePeerReplicationStatus {
#[prost(uint64, required, tag="1")]
pub next_index: u64,
#[prost(uint64, required, tag="2")]
pub match_index: u64,
#[prost(uint64, optional, tag="3")]
pub inflight_index: ::std::option::Option<u64>,
#[prost(bool, required, tag="4")]
pub probing: bool,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveIncomingXferStatus {
#[prost(message, required, tag="1")]
pub desired_range: PartitionKeyRangePb,
#[prost(message, repeated, tag="2")]
pub nodes: ::std::vec::Vec<EnclavePeerStatus>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveOutgoingXferStatus {
#[prost(bytes, required, tag="1")]
pub group_id: std::vec::Vec<u8>,
#[prost(message, required, tag="2")]
pub full_xfer_range: PartitionKeyRangePb,
#[prost(message, optional, tag="3")]
pub current_chunk_range: ::std::option::Option<PartitionKeyRangePb>,
#[prost(bool, required, tag="4")]
pub paused: bool,
#[prost(message, optional, tag="5")]
pub min_attestation: ::std::option::Option<AttestationParameters>,
#[prost(message, repeated, tag="6")]
pub nodes: ::std::vec::Vec<EnclavePeerStatus>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveFrontendStatus {
#[prost(message, optional, tag="1")]
pub memory_status: ::std::option::Option<EnclaveMemoryStatus>,
#[prost(message, repeated, tag="2")]
pub partitions: ::std::vec::Vec<EnclaveFrontendPartitionStatus>,
#[prost(message, repeated, tag="3")]
pub ranges: ::std::vec::Vec<EnclaveFrontendRangeStatus>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveFrontendPartitionStatus {
#[prost(bytes, required, tag="1")]
pub group_id: std::vec::Vec<u8>,
#[prost(message, repeated, tag="2")]
pub nodes: ::std::vec::Vec<EnclavePeerStatus>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveFrontendRangeStatus {
#[prost(message, required, tag="1")]
pub range: PartitionKeyRangePb,
#[prost(bytes, required, tag="2")]
pub group_id: std::vec::Vec<u8>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum XferControlCommand {
Start = 1,
Finish = 2,
Cancel = 3,
Pause = 4,
Resume = 5,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum UntrustedXferReplyStatus {
Unknown = 0,
Ok = 1,
NotLeader = 2,
InvalidState = 3,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum EnclaveLogLevel {
Error = 0,
Warn = 1,
Info = 2,
Debug = 3,
}

View File

@ -0,0 +1,108 @@
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Request {
#[prost(message, optional, tag="1")]
pub backup: ::std::option::Option<BackupRequest>,
#[prost(message, optional, tag="2")]
pub restore: ::std::option::Option<RestoreRequest>,
#[prost(message, optional, tag="3")]
pub delete: ::std::option::Option<DeleteRequest>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct Response {
#[prost(message, optional, tag="1")]
pub backup: ::std::option::Option<BackupResponse>,
#[prost(message, optional, tag="2")]
pub restore: ::std::option::Option<RestoreResponse>,
#[prost(message, optional, tag="3")]
pub delete: ::std::option::Option<DeleteResponse>,
}
//
// backup
//
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct BackupRequest {
#[prost(bytes, optional, tag="1")]
pub service_id: ::std::option::Option<std::vec::Vec<u8>>,
#[prost(bytes, optional, tag="2")]
pub backup_id: ::std::option::Option<std::vec::Vec<u8>>,
#[prost(bytes, optional, tag="3")]
pub nonce: ::std::option::Option<std::vec::Vec<u8>>,
#[prost(uint64, optional, tag="4")]
pub valid_from: ::std::option::Option<u64>,
#[prost(bytes, optional, tag="5")]
pub data: ::std::option::Option<std::vec::Vec<u8>>,
#[prost(bytes, optional, tag="6")]
pub pin: ::std::option::Option<std::vec::Vec<u8>>,
#[prost(uint32, optional, tag="7")]
pub tries: ::std::option::Option<u32>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct BackupResponse {
#[prost(enumeration="backup_response::Status", optional, tag="1")]
pub status: ::std::option::Option<i32>,
#[prost(bytes, optional, tag="2")]
pub nonce: ::std::option::Option<std::vec::Vec<u8>>,
}
pub mod backup_response {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum Status {
Ok = 1,
AlreadyExists = 2,
NotYetValid = 3,
}
}
//
// restore
//
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct RestoreRequest {
#[prost(bytes, optional, tag="1")]
pub service_id: ::std::option::Option<std::vec::Vec<u8>>,
#[prost(bytes, optional, tag="2")]
pub backup_id: ::std::option::Option<std::vec::Vec<u8>>,
#[prost(bytes, optional, tag="3")]
pub nonce: ::std::option::Option<std::vec::Vec<u8>>,
#[prost(uint64, optional, tag="4")]
pub valid_from: ::std::option::Option<u64>,
#[prost(bytes, optional, tag="5")]
pub pin: ::std::option::Option<std::vec::Vec<u8>>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct RestoreResponse {
#[prost(enumeration="restore_response::Status", optional, tag="1")]
pub status: ::std::option::Option<i32>,
#[prost(bytes, optional, tag="2")]
pub nonce: ::std::option::Option<std::vec::Vec<u8>>,
#[prost(bytes, optional, tag="3")]
pub data: ::std::option::Option<std::vec::Vec<u8>>,
#[prost(uint32, optional, tag="4")]
pub tries: ::std::option::Option<u32>,
}
pub mod restore_response {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum Status {
Ok = 1,
NonceMismatch = 2,
NotYetValid = 3,
Missing = 4,
PinMismatch = 5,
}
}
//
// delete
//
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct DeleteRequest {
#[prost(bytes, optional, tag="1")]
pub service_id: ::std::option::Option<std::vec::Vec<u8>>,
#[prost(bytes, optional, tag="2")]
pub backup_id: ::std::option::Option<std::vec::Vec<u8>>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct DeleteResponse {
}

View File

@ -0,0 +1,431 @@
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SecretBytes {
#[prost(bytes, required, tag="1")]
pub data: std::vec::Vec<u8>,
}
//
// transactions
//
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct TransactionData {
#[prost(oneof="transaction_data::Inner", tags="1, 2, 3, 4, 5, 6, 7, 8, 9")]
pub inner: ::std::option::Option<transaction_data::Inner>,
}
pub mod transaction_data {
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Inner {
#[prost(message, tag="1")]
FrontendRequest(super::FrontendRequestTransaction),
#[prost(message, tag="2")]
StartXfer(super::StartXferTransaction),
#[prost(message, tag="3")]
SetSid(super::SetSidTransaction),
#[prost(message, tag="4")]
RemoveChunk(super::RemoveChunkTransaction),
#[prost(message, tag="5")]
ApplyChunk(super::ApplyChunkTransaction),
#[prost(message, tag="6")]
PauseXfer(super::PauseXferTransaction),
#[prost(message, tag="7")]
ResumeXfer(super::ResumeXferTransaction),
#[prost(message, tag="8")]
FinishXfer(super::FinishXferTransaction),
#[prost(message, tag="9")]
SetTime(super::SetTimeTransaction),
}
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct FrontendRequestTransaction {
#[prost(bytes, required, tag="1")]
pub from_node_id: std::vec::Vec<u8>,
#[prost(uint64, required, tag="2")]
pub request_id: u64,
#[prost(oneof="frontend_request_transaction::Transaction", tags="3, 4, 5, 6")]
pub transaction: ::std::option::Option<frontend_request_transaction::Transaction>,
}
pub mod frontend_request_transaction {
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Transaction {
#[prost(message, tag="3")]
Create(super::CreateBackupTransaction),
#[prost(message, tag="4")]
Backup(super::BackupTransaction),
#[prost(message, tag="5")]
Restore(super::RestoreTransaction),
#[prost(message, tag="6")]
Delete(super::DeleteBackupTransaction),
}
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct CreateBackupTransaction {
#[prost(message, required, tag="1")]
pub backup_id: super::kbupd::BackupId,
#[prost(bytes, required, tag="2")]
pub new_creation_nonce: std::vec::Vec<u8>,
#[prost(bytes, required, tag="3")]
pub new_nonce: std::vec::Vec<u8>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct BackupTransaction {
#[prost(message, required, tag="1")]
pub backup_id: super::kbupd::BackupId,
#[prost(bytes, required, tag="2")]
pub old_nonce: std::vec::Vec<u8>,
#[prost(bytes, required, tag="3")]
pub new_creation_nonce: std::vec::Vec<u8>,
#[prost(bytes, required, tag="4")]
pub new_nonce: std::vec::Vec<u8>,
#[prost(message, required, tag="5")]
pub data: SecretBytes,
#[prost(message, required, tag="6")]
pub pin: SecretBytes,
#[prost(uint32, required, tag="7")]
pub tries: u32,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct RestoreTransaction {
#[prost(message, required, tag="1")]
pub backup_id: super::kbupd::BackupId,
#[prost(bytes, required, tag="2")]
pub creation_nonce: std::vec::Vec<u8>,
#[prost(bytes, required, tag="3")]
pub old_nonce: std::vec::Vec<u8>,
#[prost(bytes, required, tag="4")]
pub new_nonce: std::vec::Vec<u8>,
#[prost(message, required, tag="5")]
pub pin: SecretBytes,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct DeleteBackupTransaction {
#[prost(message, required, tag="1")]
pub backup_id: super::kbupd::BackupId,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct StartXferTransaction {
#[prost(bytes, required, tag="1")]
pub from_node_id: std::vec::Vec<u8>,
#[prost(message, required, tag="2")]
pub xfer_request: XferRequest,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SetSidTransaction {
#[prost(bytes, required, tag="1")]
pub from_node_id: std::vec::Vec<u8>,
#[prost(message, required, tag="2")]
pub service_id: super::kbupd::ServiceId,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct RemoveChunkTransaction {
#[prost(bytes, required, tag="1")]
pub from_node_id: std::vec::Vec<u8>,
#[prost(message, required, tag="2")]
pub xfer_chunk_reply: XferChunkReply,
#[prost(message, required, tag="3")]
pub chunk_last: super::kbupd::BackupId,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ApplyChunkTransaction {
#[prost(bytes, required, tag="1")]
pub from_node_id: std::vec::Vec<u8>,
#[prost(message, required, tag="2")]
pub xfer_chunk_request: XferChunkRequest,
#[prost(message, required, tag="3")]
pub xfer_chunk_reply: XferChunkReply,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct PauseXferTransaction {
#[prost(uint64, required, tag="1")]
pub request_id: u64,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ResumeXferTransaction {
#[prost(uint64, required, tag="1")]
pub request_id: u64,
#[prost(message, required, tag="2")]
pub chunk_last: super::kbupd::BackupId,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct FinishXferTransaction {
#[prost(uint64, required, tag="1")]
pub request_id: u64,
#[prost(bool, required, tag="2")]
pub force: bool,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SetTimeTransaction {
#[prost(uint64, required, tag="1")]
pub now_secs: u64,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct PeerConnectRequest {
#[prost(enumeration="NodeType", required, tag="1")]
pub node_type: i32,
#[prost(message, optional, tag="2")]
pub ias_report: ::std::option::Option<super::kbupd::IasReport>,
#[prost(bytes, required, tag="3")]
pub noise_data: std::vec::Vec<u8>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct PeerConnectReply {
#[prost(bytes, required, tag="1")]
pub sgx_quote: std::vec::Vec<u8>,
#[prost(bytes, required, tag="2")]
pub noise_data: std::vec::Vec<u8>,
}
//
// enclave-to-enclave requests
//
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveGetQuoteRequest {
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct EnclaveGetQuoteReply {
#[prost(bytes, required, tag="1")]
pub sgx_quote: std::vec::Vec<u8>,
}
//
// frontend to replica
//
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct FrontendToReplicaMessage {
#[prost(oneof="frontend_to_replica_message::Inner", tags="1, 2")]
pub inner: ::std::option::Option<frontend_to_replica_message::Inner>,
}
pub mod frontend_to_replica_message {
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Inner {
#[prost(message, tag="1")]
TransactionRequest(super::TransactionRequest),
#[prost(message, tag="2")]
EnclaveGetQuoteRequest(super::EnclaveGetQuoteRequest),
}
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct TransactionRequest {
#[prost(uint64, required, tag="1")]
pub request_id: u64,
#[prost(oneof="transaction_request::Data", tags="2, 3, 4, 5")]
pub data: ::std::option::Option<transaction_request::Data>,
}
pub mod transaction_request {
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Data {
#[prost(message, tag="2")]
Create(super::super::kbupd::CreateBackupRequest),
#[prost(message, tag="3")]
Backup(super::BackupTransactionRequest),
#[prost(message, tag="4")]
Restore(super::RestoreTransactionRequest),
#[prost(message, tag="5")]
Delete(super::DeleteTransactionRequest),
}
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct BackupTransactionRequest {
#[prost(bytes, optional, tag="1")]
pub service_id: ::std::option::Option<std::vec::Vec<u8>>,
#[prost(message, required, tag="2")]
pub backup_id: super::kbupd::BackupId,
#[prost(bytes, required, tag="3")]
pub nonce: std::vec::Vec<u8>,
#[prost(uint64, required, tag="4")]
pub valid_from: u64,
#[prost(message, required, tag="5")]
pub data: SecretBytes,
#[prost(message, required, tag="6")]
pub pin: SecretBytes,
#[prost(uint32, required, tag="7")]
pub tries: u32,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct RestoreTransactionRequest {
#[prost(bytes, optional, tag="1")]
pub service_id: ::std::option::Option<std::vec::Vec<u8>>,
#[prost(message, required, tag="2")]
pub backup_id: super::kbupd::BackupId,
#[prost(bytes, required, tag="3")]
pub nonce: std::vec::Vec<u8>,
#[prost(uint64, required, tag="4")]
pub valid_from: u64,
#[prost(message, required, tag="5")]
pub pin: SecretBytes,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct DeleteTransactionRequest {
#[prost(bytes, optional, tag="1")]
pub service_id: ::std::option::Option<std::vec::Vec<u8>>,
#[prost(message, required, tag="2")]
pub backup_id: super::kbupd::BackupId,
}
//
// replica to frontend
//
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ReplicaToFrontendMessage {
#[prost(oneof="replica_to_frontend_message::Inner", tags="1, 2")]
pub inner: ::std::option::Option<replica_to_frontend_message::Inner>,
}
pub mod replica_to_frontend_message {
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Inner {
#[prost(message, tag="1")]
TransactionReply(super::TransactionReply),
#[prost(message, tag="2")]
EnclaveGetQuoteReply(super::EnclaveGetQuoteReply),
}
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct TransactionReply {
#[prost(uint64, required, tag="1")]
pub request_id: u64,
#[prost(oneof="transaction_reply::Data", tags="2, 3, 4, 5, 6, 7, 8, 9, 10")]
pub data: ::std::option::Option<transaction_reply::Data>,
}
pub mod transaction_reply {
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Data {
#[prost(message, tag="2")]
ClientResponse(super::super::kbupd_client::Response),
#[prost(message, tag="3")]
CreateBackupReply(super::super::kbupd::CreateBackupReply),
#[prost(message, tag="4")]
DeleteBackupReply(super::super::kbupd::DeleteBackupReply),
#[prost(message, tag="5")]
NotLeader(super::TransactionErrorNotLeader),
#[prost(message, tag="6")]
WrongPartition(super::TransactionErrorWrongPartition),
#[prost(message, tag="7")]
ServiceIdMismatch(super::TransactionErrorServiceIdMismatch),
#[prost(message, tag="8")]
XferInProgress(super::TransactionErrorXferInProgress),
#[prost(message, tag="9")]
InvalidRequest(super::TransactionErrorInvalidRequest),
#[prost(message, tag="10")]
InternalError(super::TransactionErrorInternalError),
}
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct TransactionErrorNotLeader {
#[prost(bytes, optional, tag="1")]
pub leader_node_id: ::std::option::Option<std::vec::Vec<u8>>,
#[prost(message, required, tag="2")]
pub term: super::raft::TermId,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct TransactionErrorWrongPartition {
#[prost(message, optional, tag="1")]
pub range: ::std::option::Option<super::kbupd::PartitionKeyRangePb>,
#[prost(message, optional, tag="2")]
pub new_partition: ::std::option::Option<super::kbupd::PartitionConfig>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct TransactionErrorServiceIdMismatch {
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct TransactionErrorXferInProgress {
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct TransactionErrorInvalidRequest {
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct TransactionErrorInternalError {
}
//
// replica to replica
//
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct ReplicaToReplicaMessage {
#[prost(oneof="replica_to_replica_message::Inner", tags="1, 2, 8, 9, 3, 4, 5, 6, 7")]
pub inner: ::std::option::Option<replica_to_replica_message::Inner>,
}
pub mod replica_to_replica_message {
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Inner {
#[prost(message, tag="1")]
RaftMessage(super::super::raft::RaftMessage),
#[prost(message, tag="2")]
CreateRaftGroupRequest(super::CreateRaftGroupRequest),
#[prost(message, tag="8")]
EnclaveGetQuoteRequest(super::EnclaveGetQuoteRequest),
#[prost(message, tag="9")]
EnclaveGetQuoteReply(super::EnclaveGetQuoteReply),
#[prost(message, tag="3")]
XferRequest(super::XferRequest),
#[prost(message, tag="4")]
XferReply(super::XferReply),
#[prost(message, tag="5")]
XferChunkRequest(super::XferChunkRequest),
#[prost(message, tag="6")]
XferChunkReply(super::XferChunkReply),
#[prost(message, tag="7")]
XferErrorNotLeader(super::XferErrorNotLeader),
}
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct CreateRaftGroupRequest {
#[prost(message, optional, tag="1")]
pub service_id: ::std::option::Option<super::kbupd::ServiceId>,
#[prost(message, required, tag="2")]
pub group_id: super::raft::RaftGroupId,
#[prost(bytes, repeated, tag="3")]
pub node_ids: ::std::vec::Vec<std::vec::Vec<u8>>,
#[prost(message, required, tag="4")]
pub config: super::kbupd::EnclaveReplicaGroupConfig,
#[prost(message, optional, tag="5")]
pub source_partition: ::std::option::Option<super::kbupd::SourcePartitionConfig>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct XferRequest {
#[prost(uint32, required, tag="1")]
pub chunk_size: u32,
#[prost(message, required, tag="2")]
pub full_range: super::kbupd::PartitionKeyRangePb,
#[prost(bytes, repeated, tag="3")]
pub node_ids: ::std::vec::Vec<std::vec::Vec<u8>>,
#[prost(message, required, tag="4")]
pub group_id: super::raft::RaftGroupId,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct XferReply {
#[prost(message, required, tag="1")]
pub service: super::kbupd::ServiceId,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct XferChunkRequest {
#[prost(message, required, tag="1")]
pub data: SecretBytes,
#[prost(message, required, tag="2")]
pub chunk_range: super::kbupd::PartitionKeyRangePb,
#[prost(message, required, tag="3")]
pub min_attestation: super::kbupd::AttestationParameters,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct XferChunkReply {
#[prost(message, required, tag="1")]
pub new_last: super::kbupd::BackupId,
#[prost(uint32, required, tag="2")]
pub chunk_size: u32,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct XferErrorNotLeader {
#[prost(bytes, optional, tag="1")]
pub leader_node_id: ::std::option::Option<std::vec::Vec<u8>>,
#[prost(message, required, tag="2")]
pub term: super::raft::TermId,
}
//
// remote enclave handshake
//
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)]
#[repr(i32)]
pub enum NodeType {
None = 0,
Frontend = 1,
Replica = 2,
}

View File

@ -0,0 +1,4 @@
pub mod kbupd;
pub mod kbupd_client;
pub mod kbupd_enclave;
pub mod raft;

View File

@ -0,0 +1,76 @@
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct RaftMessage {
#[prost(message, required, tag="1")]
pub group: RaftGroupId,
#[prost(message, required, tag="2")]
pub term: TermId,
#[prost(oneof="raft_message::Inner", tags="3, 4, 5, 6")]
pub inner: ::std::option::Option<raft_message::Inner>,
}
pub mod raft_message {
#[derive(Clone, PartialEq, ::prost::Oneof)]
pub enum Inner {
#[prost(message, tag="3")]
VoteRequest(super::VoteRequest),
#[prost(message, tag="4")]
VoteResponse(super::VoteResponse),
#[prost(message, tag="5")]
AppendRequest(super::AppendRequest),
#[prost(message, tag="6")]
AppendResponse(super::AppendResponse),
}
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct VoteRequest {
#[prost(message, required, tag="2")]
pub last_log_idx: LogIdx,
#[prost(message, required, tag="3")]
pub last_log_term: TermId,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct VoteResponse {
#[prost(bool, required, tag="2")]
pub vote_granted: bool,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct AppendRequest {
#[prost(message, required, tag="1")]
pub prev_log_idx: LogIdx,
#[prost(message, required, tag="2")]
pub prev_log_term: TermId,
#[prost(message, required, tag="3")]
pub leader_commit: LogIdx,
#[prost(message, repeated, tag="4")]
pub entries: ::std::vec::Vec<LogEntry>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct AppendResponse {
#[prost(bool, required, tag="1")]
pub success: bool,
#[prost(message, required, tag="2")]
pub match_idx: LogIdx,
#[prost(message, required, tag="3")]
pub last_log_idx: LogIdx,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct LogEntry {
#[prost(message, required, tag="1")]
pub term: TermId,
#[prost(bytes, required, tag="2")]
pub data: std::vec::Vec<u8>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct RaftGroupId {
#[prost(bytes, required, tag="1")]
pub id: std::vec::Vec<u8>,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct TermId {
#[prost(uint64, required, tag="1")]
pub id: u64,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct LogIdx {
#[prost(uint64, required, tag="1")]
pub id: u64,
}

View File

@ -0,0 +1,210 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use std::cmp::*;
use std::fmt;
use std::ops::*;
use crate::prelude::*;
use crate::util::*;
use crate::protobufs::kbupd::*;
use crate::protobufs::kbupd_enclave;
use crate::protobufs::kbupd_client;
//
// ServiceId impls
//
impl fmt::Display for ServiceId {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { id } = self;
write!(fmt, "{}", ToHex(id))
}
}
//
// BackupId impls
//
impl BackupId {
pub const LENGTH: usize = 32;
pub fn valid_len() -> u32 {
32
}
pub fn try_from_slice<T>(slice: T) -> Result<Self, ()>
where T: AsRef<[u8]>,
{
let slice: &[u8] = slice.as_ref();
if slice.len() == Self::LENGTH {
let id = slice.to_vec();
Ok(Self { id })
} else {
Err(())
}
}
pub fn try_to_array(&self) -> Result<[u8; Self::LENGTH], ()> {
let mut array = [0; Self::LENGTH];
if self.id.len() == array.len() {
array.copy_from_slice(&self.id);
Ok(array)
} else {
Err(())
}
}
}
impl From<[u8; 32]> for BackupId {
fn from(from: [u8; 32]) -> Self {
Self { id: from.to_vec() }
}
}
impl AsRef<[u8]> for BackupId {
fn as_ref(&self) -> &[u8] {
&self.id
}
}
impl Deref for BackupId {
type Target = Vec<u8>;
fn deref(&self) -> &Self::Target {
&self.id
}
}
impl Eq for BackupId {}
impl PartialOrd for BackupId {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { Some(self.cmp(other)) }
}
impl Ord for BackupId {
fn cmp(&self, other: &Self) -> Ordering { self.id.cmp(&other.id) }
}
impl fmt::Display for BackupId {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { id } = self;
write!(fmt, "{}", ToHex(id))
}
}
//
// PartitionKeyRangePB
//
impl fmt::Display for PartitionKeyRangePb {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { first, last } = self;
write!(fmt, "{}-{}", ToHex(first), ToHex(last))
}
}
//
// UntrustedTransactionRequest
//
impl fmt::Display for UntrustedTransactionRequest {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, fmt)
}
}
//
// EnclaveFrontendRequestTransaction
//
impl enclave_frontend_request_transaction::Transaction {
pub fn from_reply(backup_id: BackupId, reply_data: &kbupd_enclave::transaction_reply::Data) -> Self {
use enclave_frontend_request_transaction::{Transaction};
use kbupd_enclave::transaction_reply::{Data as ReplyData};
use kbupd_enclave::*;
match reply_data {
ReplyData::CreateBackupReply(CreateBackupReply { .. }) =>
Transaction::Create(EnclaveCreateBackupTransaction { backup_id }),
ReplyData::ClientResponse(kbupd_client::Response { backup: Some(backup_response), .. }) =>
Transaction::Backup(EnclaveBackupTransaction {
backup_id,
status: backup_response.status.unwrap_or_default(),
}),
ReplyData::ClientResponse(kbupd_client::Response { restore: Some(restore_response), .. }) =>
Transaction::Restore(EnclaveRestoreTransaction {
backup_id,
status: restore_response.status.unwrap_or_default(),
}),
ReplyData::ClientResponse(kbupd_client::Response { delete: Some(_), .. }) |
ReplyData::DeleteBackupReply(DeleteBackupReply { .. }) =>
Transaction::Delete(EnclaveDeleteBackupTransaction { backup_id }),
ReplyData::WrongPartition(TransactionErrorWrongPartition { new_partition, .. }) =>
Transaction::WrongPartition(EnclaveTransactionErrorWrongPartition {
new_partition_unknown: new_partition.is_none(),
}),
ReplyData::XferInProgress(TransactionErrorXferInProgress {}) =>
Transaction::XferInProgress(EnclaveTransactionErrorXferInProgress {}),
ReplyData::ClientResponse(kbupd_client::Response { backup: None, restore: None, delete: None }) |
ReplyData::InvalidRequest(TransactionErrorInvalidRequest {}) =>
Transaction::InvalidRequest(EnclaveTransactionErrorInvalidRequest {}),
ReplyData::NotLeader(TransactionErrorNotLeader { .. }) |
ReplyData::ServiceIdMismatch(TransactionErrorServiceIdMismatch {}) |
ReplyData::InternalError(TransactionErrorInternalError {}) =>
Transaction::InternalError(EnclaveTransactionErrorInternalError {}),
}
}
}
//
// EnclaveFrontendConfig
//
impl fmt::Display for EnclaveFrontendConfig {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, fmt)
}
}
//
// EnclaveReplicaConfig
//
impl fmt::Display for EnclaveReplicaConfig {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(self, fmt)
}
}
//
// SourcePartitionConfig
//
impl fmt::Display for SourcePartitionConfig {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { range, node_ids } = self;
fmt.debug_struct("SourcePartitionConfig")
.field("range", &DisplayAsDebug(range))
.field("node_ids", &ListDisplay(node_ids.iter().map(|node_id| ToHex(node_id))))
.finish()
}
}

View File

@ -0,0 +1,59 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use sgx_ffi::util::{clear};
use crate::protobufs::kbupd_client::*;
//
// BackupRequest impls
//
impl Drop for BackupRequest {
fn drop(&mut self) {
if let Some(data) = &mut self.data {
clear(data);
}
if let Some(pin) = &mut self.pin {
clear(pin);
}
}
}
//
// RestoreRequest impls
//
impl Drop for RestoreRequest {
fn drop(&mut self) {
if let Some(pin) = &mut self.pin {
clear(pin);
}
}
}
//
// RestoreResponse impls
//
impl Drop for RestoreResponse {
fn drop(&mut self) {
if let Some(data) = &mut self.data {
clear(data);
}
}
}

View File

@ -0,0 +1,76 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use std::fmt;
use sgx_ffi::util::{clear};
use crate::protobufs::kbupd::*;
use crate::protobufs::kbupd_enclave::*;
use crate::util::*;
//
// SecretBytes impls
//
impl Drop for SecretBytes {
fn drop(&mut self) {
clear(&mut self.data);
}
}
//
// FrontendRequestTransaction impls
//
impl FrontendRequestTransaction {
pub fn backup_id(&self) -> Option<&BackupId> {
match &self.transaction {
Some(frontend_request_transaction::Transaction::Backup(backup)) => Some(&backup.backup_id),
Some(frontend_request_transaction::Transaction::Restore(restore)) => Some(&restore.backup_id),
Some(frontend_request_transaction::Transaction::Create(create)) => Some(&create.backup_id),
Some(frontend_request_transaction::Transaction::Delete(delete)) => Some(&delete.backup_id),
None => None
}
}
}
//
// PeerConnectRequest impls
//
impl fmt::Display for PeerConnectRequest {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt::Debug::fmt(self, fmt)
}
}
//
// XferRequest impls
//
impl fmt::Display for XferRequest {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
let Self { chunk_size, full_range, node_ids, group_id } = self;
fmt.debug_struct("XferRequest")
.field("chunk_size", chunk_size)
.field("full_range", &DisplayAsDebug(full_range))
.field("node_ids", &ListDisplay(node_ids.iter().map(|node_id| ToHex(node_id))))
.field("group_id", &DisplayAsDebug(group_id))
.finish()
}
}

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
mod kbupd;
mod kbupd_client;
mod kbupd_enclave;
mod raft;

View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::prelude::*;
use std::mem;
use sgx_ffi::util::{clear, SecretValue};
use crate::protobufs::raft::*;
//
// LogEntry impls
//
impl LogEntry {
pub fn new(term: TermId, data: SecretValue<Vec<u8>>) -> Self {
Self {
term,
data: data.into_inner(),
}
}
pub fn into_data(mut self) -> SecretValue<Vec<u8>> {
SecretValue::new(mem::replace(&mut self.data, Vec::new()))
}
}
impl Drop for LogEntry {
fn drop(&mut self) {
clear(&mut self.data);
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
syntax = "proto2";
package protobufs.raft;
message RaftMessage {
required RaftGroupId group = 1;
required TermId term = 2;
oneof inner {
VoteRequest vote_request = 3;
VoteResponse vote_response = 4;
AppendRequest append_request = 5;
AppendResponse append_response = 6;
};
}
message VoteRequest {
required LogIdx last_log_idx = 2;
required TermId last_log_term = 3;
}
message VoteResponse {
required bool vote_granted = 2;
}
message AppendRequest {
required LogIdx prev_log_idx = 1;
required TermId prev_log_term = 2;
required LogIdx leader_commit = 3;
repeated LogEntry entries = 4;
}
message AppendResponse {
required bool success = 1;
required LogIdx match_idx = 2;
required LogIdx last_log_idx = 3;
}
message LogEntry {
required TermId term = 1;
required bytes data = 2;
}
message RaftGroupId {
required bytes id = 1;
}
message TermId {
required uint64 id = 1;
}
message LogIdx {
required uint64 id = 1;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,425 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use std::collections::*;
use std::rc::*;
use hashbrown::{HashMap, hash_map};
use prost::{Message};
use rand_core::{RngCore};
use sgxsd_ffi::{RdRand};
use crate::{kbupd_send};
use crate::hasher::{DefaultHasher};
use crate::protobufs::kbupd::*;
use crate::protobufs::kbupd_enclave::*;
use crate::remote::*;
pub struct PeerManager<T> {
node_params: Rc<NodeParams>,
noise_buffers: SharedNoiseBuffers,
connecting_peers: BTreeSet<ConnectingPeerState>,
qe_info_req: QeInfoRequestState,
peers: HashMap<NodeId, Option<T>, DefaultHasher>,
total_ticks: u32,
}
pub struct PeerStarter<'a,T,U> {
peer_entry: hash_map::VacantEntry<'a, NodeId, Option<T>, DefaultHasher>,
connecting_peers: &'a mut BTreeSet<ConnectingPeerState>,
connecting_peer: ConnectingPeerState,
remote: U,
}
pub struct PeerAcceptor<'a,T> {
peer_entry: hash_map::VacantEntry<'a, NodeId, Option<T>, DefaultHasher>,
node_params: Rc<NodeParams>,
noise_buffers: SharedNoiseBuffers,
remote_node_type: NodeType,
qe_info_req: &'a mut QeInfoRequestState,
connect_request: PeerConnectRequest,
}
pub trait Peer {
type Message;
fn remote_mut(&mut self) -> &mut dyn Remote;
fn recv(&mut self, msg_data: &[u8]) -> Result<Self::Message, RemoteRecvError>;
fn send_quote_reply(&mut self, sgx_quote: EnclaveGetQuoteReply) -> Result<(), ()>;
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd)]
struct ConnectingPeerState {
next_timeout_tick: u32,
last_interval_ticks: u32,
node_id: NodeId,
}
enum QeInfoRequestState {
None,
Sent {
needs_qe_info: Vec<NodeId>,
ticks_elapsed: u32,
},
}
impl<T> PeerManager<T>
where T: Peer
{
pub fn new(node_type: NodeType) -> Self {
Self {
node_params: Rc::new(NodeParams::generate(node_type)),
noise_buffers: Default::default(),
connecting_peers: Default::default(),
qe_info_req: QeInfoRequestState::None,
peers: Default::default(),
total_ticks: Default::default(),
}
}
pub fn our_node_id(&self) -> &NodeId {
self.node_params.node_id()
}
pub fn get_peer(&self, node_id: &NodeId) -> Option<&T> {
let peer = self.peers.get(node_id)?.as_ref()?;
Some(peer)
}
pub fn get_peer_mut(&mut self, node_id: &NodeId) -> Option<&mut T> {
let peer = self.peers.get_mut(node_id)?.as_mut()?;
Some(peer)
}
pub fn remove_peer(&mut self, node_id: &NodeId) {
if let hash_map::Entry::Occupied(mut peer_entry) = self.peers.entry(node_id.clone()) {
if let Some(peer) = peer_entry.get_mut() {
if let Some(_) = peer.remote_mut().attestation() {
*peer_entry.get_mut() = None;
} else {
peer_entry.remove();
}
}
}
}
pub fn timer_tick(&mut self, min_timeout_ticks: u32, max_timeout_ticks: u32) {
self.total_ticks = self.total_ticks.wrapping_add(1);
if let QeInfoRequestState::Sent { ticks_elapsed, .. } = &mut self.qe_info_req {
*ticks_elapsed = ticks_elapsed.saturating_add(1);
if *ticks_elapsed >= min_timeout_ticks {
Self::send_get_qe_info_request();
*ticks_elapsed = Default::default();
}
}
let mut new_connecting_peers = BTreeSet::new();
while let Some(mut connecting_peer) = self.take_connecting_peer() {
if let Some(peer) = self.peers.get_mut(&connecting_peer.node_id).and_then(Option::as_mut) {
let last_interval_ticks = connecting_peer.last_interval_ticks;
let half_interval_ticks = last_interval_ticks.min(max_timeout_ticks / 2)
.max(min_timeout_ticks);
let rand_interval_ticks = RdRand.next_u32().checked_rem(half_interval_ticks)
.unwrap_or(0);
let next_timeout_ticks = half_interval_ticks.saturating_add(rand_interval_ticks);
connecting_peer.last_interval_ticks = half_interval_ticks.saturating_add(half_interval_ticks);
connecting_peer.next_timeout_tick = next_timeout_ticks.saturating_add(self.total_ticks.wrapping_add(1));
match peer.remote_mut().connect() {
Ok(()) => {
info!("connecting to peer {} with retry in {} ticks, next interval {} ticks",
peer.remote_mut().id(), next_timeout_ticks, connecting_peer.last_interval_ticks);
Self::get_qe_info(&mut self.qe_info_req, peer.remote_mut().id().clone());
new_connecting_peers.insert(connecting_peer);
}
Err(()) => (),
}
}
}
self.connecting_peers.append(&mut new_connecting_peers);
}
fn take_connecting_peer(&mut self) -> Option<ConnectingPeerState> {
if let Some(connecting_peer) = self.connecting_peers.iter().next() {
if connecting_peer.next_timeout_tick <= self.total_ticks {
let connecting_peer = connecting_peer.clone();
self.connecting_peers.remove(&connecting_peer);
Some(connecting_peer)
} else {
None
}
} else {
None
}
}
pub fn start_peer<'a,M,R>(&'a mut self,
peer_node_id: NodeId,
peer_node_type: NodeType,
auth_type: RemoteAuthorizationType)
-> Result<PeerStarter<'a, T, RemoteState<M,R>>, Option<&'a mut T>>
where M: prost::Message + 'static,
R: prost::Message + Default + 'static,
{
match self.peers.entry(peer_node_id) {
hash_map::Entry::Occupied(peer_entry) => {
Err(peer_entry.into_mut().as_mut())
}
hash_map::Entry::Vacant(peer_entry) => {
let remote = RemoteState::new(Rc::clone(&self.node_params), peer_entry.key().clone(), peer_node_type, auth_type, self.noise_buffers.clone());
let connecting_peer = ConnectingPeerState {
next_timeout_tick: self.total_ticks.wrapping_add(1),
last_interval_ticks: 0,
node_id: remote.id().clone(),
};
Ok(PeerStarter {
remote,
peer_entry,
connecting_peers: &mut self.connecting_peers,
connecting_peer,
})
}
}
}
pub fn request_quote(&mut self, peer_node_id: NodeId) {
Self::get_qe_info(&mut self.qe_info_req, peer_node_id);
}
fn get_qe_info(qe_info_req: &mut QeInfoRequestState, peer_node_id: NodeId) {
if let QeInfoRequestState::None = qe_info_req {
info!("requesting qe_info to generate quote for {}", peer_node_id);
Self::send_get_qe_info_request();
*qe_info_req = QeInfoRequestState::Sent {
needs_qe_info: Default::default(),
ticks_elapsed: Default::default(),
};
}
if let QeInfoRequestState::Sent { needs_qe_info, .. } = qe_info_req {
needs_qe_info.push(peer_node_id);
} else {
static_unreachable!();
}
}
fn send_get_qe_info_request() {
kbupd_send(EnclaveMessage {
inner: Some(enclave_message::Inner::GetQeInfoRequest(GetQeInfoRequest {})),
});
}
pub fn get_qe_info_reply(&mut self, get_qe_info_reply: GetQeInfoReply) {
if let QeInfoRequestState::Sent { needs_qe_info, .. } =
std::mem::replace(&mut self.qe_info_req, QeInfoRequestState::None)
{
info!("generating quotes for {} peers", needs_qe_info.len());
for peer_node_id in needs_qe_info {
if let Some(peer) = self.peers.get_mut(&peer_node_id).and_then(Option::as_mut) {
match peer.remote_mut().qe_info_reply(&get_qe_info_reply) {
Ok(get_quote_request) => {
kbupd_send(EnclaveMessage {
inner: Some(enclave_message::Inner::GetQuoteRequest(get_quote_request)),
});
}
Err(()) => (),
}
}
}
}
}
pub fn get_quote_reply(&mut self, get_quote_reply: GetQuoteReply) {
let peer_node_id = NodeId::from(&get_quote_reply.request_id);
if let Some(peer) = self.peers.get_mut(&peer_node_id).and_then(Option::as_mut) {
match peer.remote_mut().get_quote_reply(get_quote_reply) {
Ok(Some(get_attestation_request)) => {
kbupd_send(EnclaveMessage {
inner: Some(enclave_message::Inner::GetAttestationRequest(get_attestation_request)),
});
}
Err(Some(enclave_get_quote_reply)) => {
let _ignore = peer.send_quote_reply(enclave_get_quote_reply);
}
Ok(None) => (),
Err(None) => (),
}
}
}
pub fn request_attestation(&mut self, sgx_quote: Vec<u8>, peer_node_id: NodeId) {
info!("fetching attestation for peer {}", &peer_node_id);
kbupd_send(EnclaveMessage {
inner: Some(enclave_message::Inner::GetAttestationRequest(GetAttestationRequest {
request_id: peer_node_id.to_vec(),
sgx_quote,
})),
});
}
#[must_use]
pub fn get_attestation_reply(&mut self, get_attestation_reply: GetAttestationReply) -> Option<(&mut T, AttestationParameters)> {
let peer_node_id: NodeId = get_attestation_reply.request_id.into();
let peer = self.peers.get_mut(&peer_node_id)?.as_mut()?;
match peer.remote_mut().attestation_reply(get_attestation_reply.ias_report) {
Ok(Some(attestation)) => Some((peer, attestation)),
Ok(None) => None,
Err(()) => None,
}
}
pub fn new_message_signal(&mut self, message: NewMessageSignal) -> Result<Option<(&mut T, <T as Peer>::Message)>, PeerAcceptor<'_,T>> {
let peer_node_id: NodeId = message.node_id.into();
if message.syn {
let connect_request = match PeerConnectRequest::decode(&message.data[..]) {
Ok(connect_request) => connect_request,
Err(decode_error) => {
warn!("dropping connect request from {}: {}", &peer_node_id, decode_error);
return Ok(None);
}
};
match self.peer_connect_request(connect_request, peer_node_id) {
Some(peer_acceptor) => Err(peer_acceptor),
None => Ok(None),
}
} else {
match self.peer_message(message.data, peer_node_id) {
Ok(result) => Ok(result),
Err(()) => Ok(None),
}
}
}
fn peer_message(&mut self, message_data: Vec<u8>, peer_node_id: NodeId) -> Result<Option<(&mut T, <T as Peer>::Message)>, ()> {
let peer_entry = self.peers.get_mut(&peer_node_id);
if let Some(Some(peer)) = peer_entry {
match peer.recv(&message_data) {
Ok(message) => {
Ok(Some((peer, message)))
}
Err(RemoteRecvError::NeedsAttestation(get_attestation_request)) => {
info!("fetching attestation for peer {}", &peer_node_id);
kbupd_send(EnclaveMessage {
inner: Some(enclave_message::Inner::GetAttestationRequest(get_attestation_request)),
});
Ok(None)
}
Err(RemoteRecvError::DecodeError) |
Err(RemoteRecvError::InvalidState) => {
Err(())
}
}
} else if let Some(None) = peer_entry {
warn!("dropping message from evicted peer {}", &peer_node_id);
Err(())
} else {
error!("dropping message from missing peer {}", &peer_node_id);
Err(())
}
}
fn peer_connect_request(&mut self, connect_request: PeerConnectRequest, peer_node_id: NodeId) -> Option<PeerAcceptor<'_,T>> {
match self.peers.entry(peer_node_id) {
hash_map::Entry::Occupied(mut peer_entry) => {
if let Some(peer) = peer_entry.get_mut().as_mut() {
match peer.remote_mut().accept(connect_request) {
Ok(()) => {
Self::get_qe_info(&mut self.qe_info_req, peer_entry.key().clone());
}
Err(()) => (),
}
} else {
warn!("dropping connect request from evicted peer {}", peer_entry.key());
}
None
}
hash_map::Entry::Vacant(peer_entry) => {
if let Some(remote_node_type) = NodeType::from_i32(connect_request.node_type) {
Some(PeerAcceptor {
peer_entry,
node_params: Rc::clone(&self.node_params),
noise_buffers: self.noise_buffers.clone(),
remote_node_type,
qe_info_req: &mut self.qe_info_req,
connect_request,
})
} else {
warn!("dropping connect request from {}: invalid node type {}",
peer_entry.key(), connect_request.node_type);
None
}
}
}
}
}
//
// PeerStarter impls
//
impl<'a,T,U> PeerStarter<'a,T,U>
where T: Peer,
U: Remote
{
pub fn remote(&self) -> &U {
&self.remote
}
pub fn connect<F>(mut self, mapper: F) -> Result<&'a mut T, (Self, F)>
where F: FnOnce(U) -> T,
{
match self.remote.connect() {
Ok(()) => {
self.connecting_peers.insert(self.connecting_peer);
let peer = self.peer_entry.insert(Some(mapper(self.remote)));
Ok(peer.as_mut().unwrap_or_else(|| unreachable!()))
}
Err(()) => {
Err((self, mapper))
}
}
}
pub fn insert(self, mapper: impl FnOnce(U) -> T) -> &'a mut T {
self.peer_entry.insert(Some(mapper(self.remote)))
.as_mut()
.unwrap_or_else(|| unreachable!())
}
}
//
// PeerAcceptor impls
//
impl<'a,T> PeerAcceptor<'a,T>
where T: Peer
{
pub fn node_id(&self) -> &NodeId {
self.peer_entry.key()
}
pub fn connect_request(&self) -> &PeerConnectRequest {
&self.connect_request
}
pub fn accept<M,R>(self, mapper: impl FnOnce(RemoteState<M,R>) -> T, auth_type: RemoteAuthorizationType) -> Result<&'a mut T, ()>
where M: prost::Message + 'static,
R: prost::Message + Default + 'static,
{
let mut remote = RemoteState::new(Rc::clone(&self.node_params), self.peer_entry.key().clone(), self.remote_node_type, auth_type, self.noise_buffers);
remote.accept(self.connect_request)?;
PeerManager::<T>::get_qe_info(self.qe_info_req, self.peer_entry.key().clone());
let peer = self.peer_entry.insert(Some(mapper(remote)));
Ok(peer.as_mut().unwrap_or_else(|| unreachable!()))
}
}

View File

@ -0,0 +1,156 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use bytes::{Buf};
use num_traits::{ToPrimitive};
const SGX_FLAGS_INITTED: u64 = 0x0000_0000_0000_0001;
const SGX_FLAGS_DEBUG: u64 = 0x0000_0000_0000_0002;
const SGX_FLAGS_MODE64BIT: u64 = 0x0000_0000_0000_0004;
const SGX_FLAGS_RESERVED: u64 = 0xFFFF_FFFF_FFFF_FFC8;
const SGX_XFRM_RESERVED: u64 = 0xFFFF_FFFF_FFFF_FFF8;
#[derive(Default)]
pub struct SgxQuote {
pub version: u16,
pub is_sig_linkable: bool,
pub gid: u32,
pub qe_svn: u16,
pub pce_svn: u16,
pub basename: [u8; 32],
pub cpu_svn: [u8; 16],
pub flags: u64,
pub xfrm: u64,
pub mrenclave: [u8; 32],
pub mrsigner: [u8; 32],
pub isv_prod_id: u16,
pub isv_svn: u16,
pub report_data: SgxReportData,
pub has_signature: bool,
}
pub struct SgxReportData(pub [u8; 64]);
impl Default for SgxReportData {
fn default() -> Self {
Self([0; 64])
}
}
#[derive(Debug)]
pub enum SgxQuoteDecodeError {
Truncated(usize),
NonZeroReserved(usize),
UnknownVersion(u16),
UnknownSignType(u16),
InvalidFlags(u64),
InvalidXfrm(u64),
InvalidSignatureLength(u32),
}
impl SgxQuote {
pub fn decode(quote_buf: &mut impl Buf) -> Result<Self, SgxQuoteDecodeError> {
if quote_buf.remaining() < 432 {
return Err(SgxQuoteDecodeError::Truncated(quote_buf.remaining()));
}
let mut quote: Self = Default::default();
quote.version = quote_buf.get_u16_le();
if !(quote.version >= 1 && quote.version <= 2) {
return Err(SgxQuoteDecodeError::UnknownVersion(quote.version));
}
let sign_type = quote_buf.get_u16_le();
if (sign_type & !1) != 0 {
return Err(SgxQuoteDecodeError::UnknownSignType(sign_type));
}
quote.is_sig_linkable = sign_type == 1;
quote.gid = quote_buf.get_u32_le();
quote.qe_svn = quote_buf.get_u16_le();
if quote.version > 1 {
quote.pce_svn = quote_buf.get_u16_le();
} else {
Self::read_zero(quote_buf, 10, 2)?;
quote.pce_svn = 0;
}
Self::read_zero(quote_buf, 12, 4)?; // xeid (reserved)
quote_buf.copy_to_slice(&mut quote.basename);
//
// report_body
//
quote_buf.copy_to_slice(&mut quote.cpu_svn);
Self::read_zero(quote_buf, 64, 4)?; // misc_select (reserved)
Self::read_zero(quote_buf, 68, 28)?; // reserved1
quote.flags = quote_buf.get_u64_le();
if ((quote.flags & SGX_FLAGS_RESERVED ) != 0 ||
(quote.flags & SGX_FLAGS_INITTED ) == 0 ||
(quote.flags & SGX_FLAGS_MODE64BIT) == 0)
{
return Err(SgxQuoteDecodeError::InvalidFlags(quote.flags));
}
quote.xfrm = quote_buf.get_u64_le();
if (quote.xfrm & SGX_XFRM_RESERVED) != 0 {
return Err(SgxQuoteDecodeError::InvalidXfrm(quote.xfrm));
}
quote_buf.copy_to_slice(&mut quote.mrenclave);
Self::read_zero(quote_buf, 144, 32)?; // reserved2
quote_buf.copy_to_slice(&mut quote.mrsigner);
Self::read_zero(quote_buf, 208, 96)?; // reserved3
quote.isv_prod_id = quote_buf.get_u16_le();
quote.isv_svn = quote_buf.get_u16_le();
Self::read_zero(quote_buf, 308, 60)?; // reserved4
quote_buf.copy_to_slice(&mut quote.report_data.0);
//
// quote signature
//
if quote_buf.remaining() != 0 {
let signature_length = quote_buf.get_u32_le();
if quote_buf.remaining().to_u32() != Some(signature_length) {
return Err(SgxQuoteDecodeError::InvalidSignatureLength(signature_length));
}
quote.has_signature = true;
} else {
quote.has_signature = false;
}
Ok(quote)
}
pub fn is_debug_quote(&self) -> bool {
(self.flags & SGX_FLAGS_DEBUG) != 0
}
fn read_zero(buf: &mut impl Buf, pos: usize, count: usize) -> Result<(), SgxQuoteDecodeError> {
for _ in 0..count {
if buf.get_u8() != 0 {
return Err(SgxQuoteDecodeError::NonZeroReserved(pos));
}
}
Ok(())
}
}

View File

@ -0,0 +1,426 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::prelude::*;
use std::collections::*;
use std::fmt;
use std::rc::*;
use rand_core::{RngCore};
use sgxsd_ffi::{RdRand};
use crate::protobufs::kbupd::*;
use crate::protobufs::kbupd_enclave::*;
use crate::protobufs::raft::*;
use crate::remote::*;
use crate::util::*;
pub trait RemoteGroupPendingRequest {
type RequestId: Clone + Ord + Eq;
type Message: prost::Message;
fn request_id(&self) -> &Self::RequestId;
fn message(&self) -> Rc<Self::Message>;
fn min_attestation(&self) -> Option<AttestationParameters>;
}
pub trait RemoteGroupNode {
fn request_quote(&mut self, request: EnclaveGetQuoteRequest) -> Result<(), ()>;
}
pub struct RemoteGroupState<T,R>
where R: RemoteGroupPendingRequest,
{
name: String,
nodes: Box<[RemoteGroupNodeState<T, R::RequestId>]>,
leader: Option<usize>,
term: TermId,
pending: BTreeMap<R::RequestId, PendingRequestState<R>>,
timeout_ticks: u32,
request_quote_ticks: u32,
total_ticks: u32,
}
pub enum RemoteGroupSendError<R> {
NotYetValid(R),
AlreadySent(R),
}
struct PendingRequestState<R> {
request: R,
sent_at_tick: u32,
}
//
// RemoteGroupState impls
//
struct RemoteGroupNodeState<T, RequestId> {
remote: T,
last_sent: Option<RequestId>,
}
impl<T,R> RemoteGroupState<T,R>
where T: RemoteMessageSender<Message = R::Message> + 'static,
T: RemoteGroupNode,
R: RemoteGroupPendingRequest + 'static,
{
pub fn new(name: String, remotes: Vec<T>) -> Self {
let nodes = remotes.into_iter().map(|remote: T| RemoteGroupNodeState {
remote,
last_sent: Default::default(),
});
Self {
name,
nodes: nodes.collect::<Vec<_>>().into(),
leader: Default::default(),
term: Default::default(),
pending: Default::default(),
timeout_ticks: Default::default(),
request_quote_ticks: Default::default(),
total_ticks: Default::default(),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn pending_len(&self) -> usize {
self.pending.len()
}
pub fn trim_to(&mut self, trim_to_len: usize, ttl_ticks: u32) -> impl DoubleEndedIterator<Item = R> + ExactSizeIterator {
let mut maybe_trim_key: Option<&R::RequestId> = None;
if let Some(trim_count) = self.pending.len().checked_sub(trim_to_len) {
for (pending_request_id, pending_request) in self.pending.iter().take(trim_count) {
if self.total_ticks.checked_sub(pending_request.sent_at_tick) < Some(ttl_ticks) {
break;
}
maybe_trim_key = Some(pending_request_id);
}
}
let trimmed = if let Some(trim_key) = maybe_trim_key {
let split_off = if let Some((new_first_key, _)) = self.pending.range(trim_key..).nth(1) {
let new_first_key = new_first_key.clone();
self.pending.split_off(&new_first_key)
} else {
BTreeMap::new()
};
std::mem::replace(&mut self.pending, split_off)
} else {
BTreeMap::new()
};
trimmed.into_iter()
.map(|(_, pending_request_state): (_, PendingRequestState<R>)| {
pending_request_state.request
})
}
pub fn reset_peer(&mut self, node_id: &NodeId) {
match self.get_node_mut(node_id) {
Some(from_node) => {
from_node.last_sent = Default::default();
}
None => return,
}
warn!("resetting group {} peer {}", &self.name, node_id);
let maybe_old_leader =
self.leader.and_then(|leader: usize| self.nodes.get(leader))
.map(|leader: &RemoteGroupNodeState<T, _>| leader.remote.id());
if maybe_old_leader == Some(node_id) {
self.leader = None;
self.choose_leader();
}
self.flush_requests();
}
pub fn contains_authorized_node(&self, node_id: &NodeId) -> bool {
if let Some(node) = self.get_node(node_id) {
node.remote.attestation().is_some()
} else {
false
}
}
pub fn status(&self) -> Vec<EnclavePeerStatus> {
let mut statuses = Vec::with_capacity(self.nodes.len());
for (node_idx, node) in self.nodes.iter().enumerate() {
let unsent_requests = if let Some(last_sent) = &node.last_sent {
self.pending.range(last_sent..).count().saturating_sub(1)
} else {
self.pending.len()
};
let inflight_requests = self.pending.len().saturating_sub(unsent_requests);
statuses.push(EnclavePeerStatus {
node_id: node.remote.id().to_vec(),
attestation: node.remote.attestation(),
replication_status: None,
is_leader: Some(node_idx) == self.leader,
unsent_requests: unsent_requests.to_u64(),
inflight_requests: inflight_requests.to_u64(),
})
}
statuses
}
fn get_node_mut(&mut self, node_id: &NodeId) -> Option<&mut RemoteGroupNodeState<T, R::RequestId>> {
self.nodes.iter_mut().find_map(|node: &mut RemoteGroupNodeState<T, _>| {
if node.remote.id() == node_id {
Some(node)
} else {
None
}
})
}
fn get_node(&self, node_id: &NodeId) -> Option<&RemoteGroupNodeState<T, R::RequestId>> {
self.nodes.iter().find_map(|node: &RemoteGroupNodeState<T, _>| {
if node.remote.id() == node_id {
Some(node)
} else {
None
}
})
}
fn get_leader_node(&self) -> Option<&RemoteGroupNodeState<T, R::RequestId>> {
if let Some(leader) = &self.leader {
self.nodes.get(*leader)
} else {
None
}
}
pub fn timer_tick(&mut self, max_timeout_ticks: u32, max_request_quote_ticks: u32) {
self.timeout_ticks = self.timeout_ticks.saturating_add(1);
self.request_quote_ticks = self.request_quote_ticks.saturating_add(1);
self.total_ticks = self.total_ticks.wrapping_add(1);
if self.timeout_ticks >= max_timeout_ticks {
self.timeout_ticks = Default::default();
if !self.pending.is_empty() {
if let Some(leader) = self.get_leader_node() {
info!("group {} timeout on leader {}", &self.name, leader.remote.id());
self.leader = None;
}
self.choose_leader();
}
}
if self.request_quote_ticks >= max_request_quote_ticks {
self.request_quote_ticks = Default::default();
for node in &mut self.nodes[..] {
let _ignore = node.remote.request_quote(EnclaveGetQuoteRequest {});
}
}
}
pub fn remote_authorized(&mut self, node_id: &NodeId) {
if self.get_node(node_id).is_some() {
self.choose_leader();
}
}
pub fn remote_not_leader(&mut self, term: TermId, maybe_new_leader: Option<&NodeId>, from_node_id: &NodeId) {
match self.get_node_mut(from_node_id) {
Some(from_node) => {
from_node.last_sent = Default::default();
}
None => return,
}
let nodes = &self.nodes[..];
let maybe_old_leader =
self.leader.and_then(|leader: usize| nodes.get(leader))
.map(|leader: &RemoteGroupNodeState<T, _>| leader.remote.id());
if term >= self.term {
self.term = term;
// prevent re-send storm from a node responding NotLeader while contradictorily asserting itself as leader
if let Some(new_leader) = maybe_new_leader.filter(|new_leader: &&NodeId| new_leader != &from_node_id) {
if Some(new_leader) != maybe_old_leader {
info!("group {} changed leader to {} at term {}", &self.name, new_leader, &self.term.id);
self.leader = nodes.iter().position(|node: &RemoteGroupNodeState<T, _>| node.remote.id() == new_leader);
}
} else if let Some(old_leader) = maybe_old_leader {
info!("group {} lost leader {} at term {}", &self.name, old_leader, &self.term.id);
self.leader = None;
}
} else if let Some(old_leader) = maybe_old_leader {
if old_leader == from_node_id {
info!("group {} lost leader {} at term {}", &self.name, old_leader, &self.term.id);
self.leader = None;
}
}
self.flush_requests();
}
pub fn send(&mut self, request: R) -> Result<(), RemoteGroupSendError<R>> {
let request_id = request.request_id().clone();
let message = request.message();
if Some(&request_id) < self.pending.keys().last() {
return Err(RemoteGroupSendError::AlreadySent(request));
}
let nodes = &mut self.nodes[..];
let maybe_authorized_leader =
self.leader.and_then(|leader: usize| nodes.get_mut(leader))
.filter(|leader: &&mut RemoteGroupNodeState<T, _>| leader.remote.attestation().is_some());
if let btree_map::Entry::Vacant(pending_request_entry) = self.pending.entry(request_id) {
let sent_at_tick = self.total_ticks;
if let Some(authorized_leader) = maybe_authorized_leader {
if request.min_attestation() <= authorized_leader.remote.attestation() {
authorized_leader.send(message);
authorized_leader.mark_sent(Some(request.request_id()));
pending_request_entry.insert(PendingRequestState { request, sent_at_tick });
Ok(())
} else {
Err(RemoteGroupSendError::NotYetValid(request))
}
} else {
pending_request_entry.insert(PendingRequestState { request, sent_at_tick });
Ok(())
}
} else {
Err(RemoteGroupSendError::AlreadySent(request))
}
}
pub fn handle_reply(&mut self, request_id: &R::RequestId) -> Option<R> {
self.timeout_ticks = Default::default();
self.pending.remove(request_id)
.map(|request_state: PendingRequestState<R>| request_state.request)
}
pub fn get_remotes(&self) -> Vec<NodeId> {
self.nodes[..].iter().map(|node: &RemoteGroupNodeState<T, _>| node.remote.id().clone()).collect()
}
#[allow(clippy::indexing_slicing, clippy::integer_arithmetic)]
fn choose_leader(&mut self) {
if self.get_leader_node().is_none() {
let mut nodes: Vec<usize> = (0..self.nodes.len()).collect();
for nodes_idx in 0..nodes.len() {
let rand = (RdRand.next_u32().to_usize()) % (self.nodes.len() - nodes_idx);
nodes.swap(nodes_idx, nodes_idx.wrapping_add(rand));
let node_idx = nodes[nodes_idx];
let node = &self.nodes[node_idx];
if (node.remote.attestation().is_some() &&
self.has_unsent_to(node))
{
self.leader = Some(node_idx);
self.timeout_ticks = Default::default();
info!("group {} chose random leader {}", &self.name, node.remote.id());
break;
}
}
}
self.flush_requests();
}
fn flush_requests(&mut self) {
let nodes = &mut self.nodes[..];
let maybe_authorized_leader =
self.leader.and_then(|leader: usize| nodes.get_mut(leader))
.filter(|leader: &&mut RemoteGroupNodeState<T, _>| leader.remote.attestation().is_some());
if let Some(authorized_leader) = maybe_authorized_leader {
let mut queue = Vec::new();
let mut not_yet_valid_count: u64 = 0;
let mut last_sent_request_id: Option<&R::RequestId> = None;
for pending_request in self.pending.values_mut() {
if !authorized_leader.has_sent(&pending_request.request) {
if pending_request.request.min_attestation() <= authorized_leader.remote.attestation() {
last_sent_request_id = Some(pending_request.request.request_id());
queue.push(pending_request.request.message());
} else {
not_yet_valid_count += 1;
}
}
}
if not_yet_valid_count > 0 {
info!("group {} not sending {} messages to new leader {} due to attestation timestamp {}",
&self.name, not_yet_valid_count, authorized_leader.remote.id(),
OptionDisplay(authorized_leader.remote.attestation().as_ref()));
}
if !queue.is_empty() {
info!("group {} resending {} messages to new leader {}",
&self.name, queue.len(), authorized_leader.remote.id());
for message in queue {
authorized_leader.send(message);
}
}
authorized_leader.mark_sent(last_sent_request_id);
}
}
fn has_unsent_to(&self, node: &RemoteGroupNodeState<T, R::RequestId>) -> bool {
if let Some(last_request) = self.pending.values().last() {
!node.has_sent(&last_request.request)
} else {
true
}
}
}
impl<T, RequestId> RemoteGroupNodeState<T, RequestId>
where T: RemoteMessageSender + 'static,
RequestId: Clone + Ord + Eq,
{
fn send(&self, request: Rc<T::Message>) {
let _ignore = self.remote.send(request);
}
fn mark_sent(&mut self, sent_request_id: Option<&RequestId>) {
if sent_request_id > self.last_sent.as_ref() {
self.last_sent = sent_request_id.cloned();
}
}
fn has_sent<R>(&self, request: &R) -> bool
where R: RemoteGroupPendingRequest<RequestId = RequestId> + 'static,
{
Some(request.request_id()) <= self.last_sent.as_ref()
}
}
impl<T,R> fmt::Display for RemoteGroupState<T,R>
where T: RemoteMessageSender<Message = R::Message> + RemoteGroupNode + 'static,
R: RemoteGroupPendingRequest + 'static,
{
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("RemoteGroupState")
.field("name", &self.name)
.field("nodes", &ListDisplay(self.nodes.iter().map(|node| node.remote.id())))
.field("leader", &OptionDisplay(self.get_leader_node().map(|node| node.remote.id())))
.field("term", &DisplayAsDebug(self.term))
.finish()
}
}
impl<R> fmt::Display for RemoteGroupSendError<R> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let name = match self {
RemoteGroupSendError::AlreadySent(_) => "AlreadySent",
RemoteGroupSendError::NotYetValid(_) => "NotYetValid",
};
write!(fmt, "{}", name)
}
}

View File

@ -0,0 +1,955 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::prelude::*;
use std::cmp::{Ordering};
use std::ops::{Add};
use std::rc::*;
use std::time::*;
use hashbrown::{HashMap};
use prost::{Message};
use sgx_ffi::util::{SecretValue};
use sgxsd_ffi::ecalls::{SgxsdMsgFrom};
use crate::{kbupd_send};
use crate::ffi::ecalls::*;
use crate::hasher::{DefaultHasher};
use crate::protobufs::kbupd::*;
use crate::protobufs::kbupd_client;
use crate::protobufs::kbupd_enclave::*;
use crate::protobufs::raft::*;
use crate::util::*;
use crate::remote::*;
use crate::remote_group::*;
use crate::service::replica::{PartitionKeyRange};
const NODE_TYPE: NodeType = NodeType::Frontend;
//
// data structures
//
pub struct FrontendState {
config: EnclaveFrontendConfig,
replicas: PeerManager<Replica>,
partitions: HashMap<RaftGroupId, Partition, DefaultHasher>,
key_ranges: PartitionKeyRanges,
last_request_id: PendingRequestId,
}
type RemoteReplicaMessageSender = RemoteSender<FrontendToReplicaMessage>;
struct Replica {
remote: RemoteState<FrontendToReplicaMessage, ReplicaToFrontendMessage>,
group_id: RaftGroupId,
}
struct Partition {
remote_group: RemoteGroupState<RemoteReplicaMessageSender, PendingRequest>,
}
#[derive(Default)]
struct PartitionKeyRanges {
ranges: Vec<(PartitionKeyRange, RaftGroupId)>,
}
#[derive(Clone, Default, Eq, PartialEq, Ord, PartialOrd)]
struct PendingRequestId {
id: u64,
}
#[allow(variant_size_differences)]
enum PendingRequestFrom {
Client(PendingClientRequest),
Untrusted {
untrusted_request_id: u64,
}
}
struct PendingRequest {
id: PendingRequestId,
message: Rc<FrontendToReplicaMessage>,
min_attestation: Option<AttestationParameters>,
from: PendingRequestFrom,
}
pub struct PendingClientRequest {
from: SgxsdMsgFrom,
}
//
// FrontendState impls
//
impl FrontendState {
pub fn init(request: StartFrontendRequest) -> Self {
let mut state = Self {
config: request.config,
replicas: PeerManager::new(NODE_TYPE),
partitions: Default::default(),
key_ranges: Default::default(),
last_request_id: Default::default(),
};
for partition_config in request.partitions {
state.update_partition(partition_config);
}
kbupd_send(EnclaveMessage {
inner: Some(enclave_message::Inner::StartFrontendReply(StartFrontendReply {
node_id: state.replicas.our_node_id().to_vec(),
})),
});
state
}
fn update_partition(&mut self, config: PartitionConfig) {
let PartitionConfig { group_id, range, node_ids } = config;
let group_id = RaftGroupId { id: group_id };
if let Some(range) = range {
match PartitionKeyRange::try_from_pb(&range) {
Ok(range) => {
self.key_ranges.update(&group_id, range);
}
Err(()) => {
error!("received partition config for {} with invalid range: {}", &group_id, &range);
return;
}
}
} else {
self.key_ranges.remove(&group_id);
}
if !self.partitions.contains_key(&group_id) {
let mut remotes = Vec::with_capacity(node_ids.len());
for replica_node_id in node_ids {
if let Some(replica) = self.start_replica_remote(replica_node_id[..].into(), group_id.clone()) {
remotes.push(replica.remote.sender().clone());
} else {
warn!("couldnt connect to evicted replica {}", NodeId::from(replica_node_id));
}
}
let group_name = format!("{}", ToHex(&group_id.id));
self.partitions.insert(group_id, Partition {
remote_group: RemoteGroupState::new(group_name, remotes),
});
};
}
fn start_replica_remote(&mut self, node_id: NodeId, group_id: RaftGroupId) -> Option<&Replica> {
match self.replicas.start_peer(node_id, NodeType::Replica, RemoteAuthorizationType::RemoteOnly) {
Ok(replica_entry) => {
match replica_entry.connect(|remote| Replica { remote, group_id }) {
Ok(replica) => Some(replica),
Err((replica_entry, mapper)) => {
warn!("inserting disconnected replica entry for {} due to connect error",
replica_entry.remote().id());
Some(replica_entry.insert(mapper))
}
}
}
Err(Some(replica)) => Some(replica),
Err(None) => None,
}
}
//
// untrusted messages
//
pub fn untrusted_message(&mut self, untrusted_message: UntrustedMessage) {
match untrusted_message.inner {
Some(untrusted_message::Inner::StartFrontendRequest(_)) |
Some(untrusted_message::Inner::StartReplicaRequest(_)) =>
(),
Some(untrusted_message::Inner::StartReplicaGroupRequest(_)) =>
(),
Some(untrusted_message::Inner::UntrustedTransactionRequest(request)) =>
self.handle_untrusted_transaction_request(request),
Some(untrusted_message::Inner::UntrustedXferRequest(_)) =>
(),
Some(untrusted_message::Inner::GetEnclaveStatusRequest(request)) =>
self.handle_get_enclave_status_request(request),
Some(untrusted_message::Inner::GetQeInfoReply(reply)) =>
self.handle_get_qe_info_reply(reply),
Some(untrusted_message::Inner::GetQuoteReply(reply)) =>
self.handle_get_quote_reply(reply),
Some(untrusted_message::Inner::GetAttestationReply(reply)) =>
self.handle_get_attestation_reply(reply),
Some(untrusted_message::Inner::NewMessageSignal(signal)) =>
self.handle_new_message_signal(signal),
Some(untrusted_message::Inner::TimerTickSignal(signal)) =>
self.handle_timer_tick_signal(signal),
Some(untrusted_message::Inner::SetFrontendConfigSignal(signal)) =>
self.handle_set_frontend_config_signal(signal),
Some(untrusted_message::Inner::SetReplicaConfigSignal(_)) =>
(),
Some(untrusted_message::Inner::ResetPeerSignal(signal)) =>
self.handle_reset_peer_signal(signal),
Some(untrusted_message::Inner::SetVerboseLoggingSignal(signal)) =>
self.handle_set_verbose_logging_signal(signal),
None => (),
}
}
fn handle_untrusted_transaction_request(&mut self, request: UntrustedTransactionRequest) {
let from = PendingRequestFrom::Untrusted {
untrusted_request_id: request.request_id,
};
match validate_untrusted_transaction_request(request.data) {
Ok(transaction_request_data) => {
self.request_transaction(transaction_request_data, from);
}
Err(()) => {
error!("received invalid untrusted transaction request");
self.cancel_pending_request(from);
}
}
}
fn handle_get_enclave_status_request(&mut self, request: GetEnclaveStatusRequest) {
let memory_status = if request.memory_status {
Some(memory_status())
} else {
None
};
let mut partitions = Vec::with_capacity(self.partitions.len());
for (group_id, partition) in &self.partitions {
partitions.push(EnclaveFrontendPartitionStatus {
group_id: group_id.id.clone(),
nodes: partition.remote_group.status(),
});
}
let mut ranges = Vec::with_capacity(self.key_ranges.ranges.len());
for (range, group_id) in &self.key_ranges.ranges {
ranges.push(EnclaveFrontendRangeStatus {
range: range.to_pb(),
group_id: group_id.id.clone(),
});
}
kbupd_send(EnclaveMessage {
inner: Some(enclave_message::Inner::GetEnclaveStatusReply(GetEnclaveStatusReply {
inner: Some(get_enclave_status_reply::Inner::FrontendStatus(EnclaveFrontendStatus {
memory_status,
partitions,
ranges,
})),
})),
});
}
fn handle_get_qe_info_reply(&mut self, reply: GetQeInfoReply) {
self.replicas.get_qe_info_reply(reply);
}
fn handle_get_quote_reply(&mut self, reply: GetQuoteReply) {
self.replicas.get_quote_reply(reply);
}
fn handle_get_attestation_reply(&mut self, reply: GetAttestationReply) {
if let Some((Replica { remote, .. }, attestation)) = self.replicas.get_attestation_reply(reply) {
let peer_node_id = remote.id().clone();
self.replica_authorized(attestation, peer_node_id);
}
}
fn replica_authorized(&mut self, _attestation: AttestationParameters, replica_node_id: NodeId) {
if let Some((_replica, partition)) = Self::get_partition_replica_mut(&mut self.replicas, &mut self.partitions, &replica_node_id) {
partition.remote_group.remote_authorized(&replica_node_id);
}
}
fn handle_new_message_signal(&mut self, signal: NewMessageSignal) {
match self.replicas.new_message_signal(signal) {
Ok(Some((from, message))) => {
let from_node_id = from.remote_mut().id().clone();
self.replica_message(message, from_node_id);
}
Ok(None) => (),
Err(peer_entry) => {
warn!("unsolicited connect request from {}: {}", peer_entry.node_id(), peer_entry.connect_request())
}
}
}
fn handle_timer_tick_signal(&mut self, _signal: TimerTickSignal) {
self.replicas.timer_tick(self.config.min_connect_timeout_ticks, self.config.max_connect_timeout_ticks);
for partition in self.partitions.values_mut() {
partition.remote_group.timer_tick(self.config.replica_timeout_ticks, self.config.request_quote_ticks);
}
}
fn handle_set_frontend_config_signal(&mut self, signal: SetFrontendConfigSignal) {
info!("setting frontend config to {:#}", &signal.config);
self.config = signal.config;
}
fn handle_reset_peer_signal(&mut self, signal: ResetPeerSignal) {
let node_id: NodeId = signal.peer_node_id.into();
if let Some((_replica, partition)) = Self::get_partition_replica_mut(&mut self.replicas, &mut self.partitions, &node_id) {
partition.remote_group.reset_peer(&node_id);
}
}
fn handle_set_verbose_logging_signal(&mut self, signal: SetVerboseLoggingSignal) {
crate::logging::set_verbose_logging_enabled(signal.verbose_logging);
}
//
// replica messages
//
fn replica_message(&mut self, replica_message: ReplicaToFrontendMessage, from_node_id: NodeId) {
match replica_message.inner {
Some(replica_to_frontend_message::Inner::TransactionReply(transaction_reply)) =>
self.handle_transaction_reply(transaction_reply, from_node_id),
Some(replica_to_frontend_message::Inner::EnclaveGetQuoteReply(reply)) =>
self.handle_enclave_get_quote_reply(reply, from_node_id),
None => (),
}
}
fn handle_transaction_reply(&mut self, transaction_reply: TransactionReply, from_node_id: NodeId) {
if let Some((replica, partition)) = Self::get_partition_replica_mut(&mut self.replicas, &mut self.partitions, &from_node_id) {
match transaction_reply.data {
Some(transaction_reply::Data::ClientResponse(client_reply)) => {
let maybe_pending_request: Option<PendingRequest> =
partition.remote_group.handle_reply(&PendingRequestId { id: transaction_reply.request_id });
if let Some(PendingRequestFrom::Client(pending_client_request)) =
maybe_pending_request.map(|pending_request| pending_request.from)
{
pending_client_request.reply(&client_reply);
} else {
info!("pending client request {} not found", transaction_reply.request_id);
}
}
Some(transaction_reply::Data::InvalidRequest(_invalid_request_error)) => {
error!("replica {} reported InvalidRequest {}", &from_node_id, &transaction_reply.request_id);
let maybe_pending_request: Option<PendingRequest> =
partition.remote_group.handle_reply(&PendingRequestId { id: transaction_reply.request_id });
if let Some(pending_request) = maybe_pending_request {
self.cancel_pending_request(pending_request.from);
}
}
Some(transaction_reply::Data::InternalError(_internal_error)) => {
warn!("replica {} reported InternalError on request {}!",
&from_node_id, &transaction_reply.request_id);
let maybe_pending_request: Option<PendingRequest> =
partition.remote_group.handle_reply(&PendingRequestId { id: transaction_reply.request_id });
if let Some(pending_request) = maybe_pending_request {
self.cancel_pending_request(pending_request.from);
}
}
Some(transaction_reply::Data::CreateBackupReply(create_backup_reply)) => {
let maybe_pending_request: Option<PendingRequest> =
partition.remote_group.handle_reply(&PendingRequestId { id: transaction_reply.request_id });
if let Some(PendingRequestFrom::Untrusted { untrusted_request_id }) =
maybe_pending_request.map(|pending_request| pending_request.from)
{
kbupd_send(EnclaveMessage {
inner: Some(enclave_message::Inner::UntrustedTransactionReply(UntrustedTransactionReply {
request_id: untrusted_request_id,
data: Some(untrusted_transaction_reply::Data::CreateBackupReply(create_backup_reply)),
})),
});
} else {
info!("pending untrusted transaction request {} not found",
transaction_reply.request_id);
}
}
Some(transaction_reply::Data::DeleteBackupReply(delete_backup_reply)) => {
let maybe_pending_request: Option<PendingRequest> =
partition.remote_group.handle_reply(&PendingRequestId { id: transaction_reply.request_id });
match maybe_pending_request.map(|pending_request| pending_request.from) {
Some(PendingRequestFrom::Client(pending_client_request)) => {
pending_client_request.reply(&kbupd_client::Response {
backup: None,
restore: None,
delete: Some(kbupd_client::DeleteResponse {}),
});
}
Some(PendingRequestFrom::Untrusted { untrusted_request_id }) => {
kbupd_send(EnclaveMessage {
inner: Some(enclave_message::Inner::UntrustedTransactionReply(UntrustedTransactionReply {
request_id: untrusted_request_id,
data: Some(untrusted_transaction_reply::Data::DeleteBackupReply(delete_backup_reply)),
})),
});
}
None => {
info!("pending client request {} not found", transaction_reply.request_id);
}
}
}
Some(transaction_reply::Data::NotLeader(not_leader_error_data)) => {
let new_leader: Option<NodeId> = not_leader_error_data.leader_node_id.map(NodeId::from);
partition.remote_group.remote_not_leader(not_leader_error_data.term, new_leader.as_ref(), &from_node_id);
verbose!("replica {} reported NotLeader for partition {} with new leader {} at term {}",
replica.remote.id(), &replica.group_id, OptionDisplay(new_leader.as_ref()),
&not_leader_error_data.term);
}
Some(transaction_reply::Data::WrongPartition(wrong_partition_error_data)) => {
let maybe_pending_request: Option<PendingRequest> =
partition.remote_group.handle_reply(&PendingRequestId { id: transaction_reply.request_id });
if let Some(range) = &wrong_partition_error_data.range {
match PartitionKeyRange::try_from_pb(range) {
Ok(range) => {
info!("partition {} reported WrongPartition with range {}", &replica.group_id, range);
self.key_ranges.update(&replica.group_id, range);
}
Err(()) => {
error!("partition {} reported WrongPartition with invalid range {}",
&replica.group_id, range);
}
}
} else {
info!("partition {} reported WrongPartition with no range", &replica.group_id);
self.key_ranges.remove(&replica.group_id);
}
if let Some(new_partition) = wrong_partition_error_data.new_partition {
info!("partition {} reported WrongPartition with new partition {} and range {}",
&replica.group_id, ToHex(&new_partition.group_id), OptionDisplay(new_partition.range.as_ref()));
self.update_partition(new_partition);
} else {
warn!("partition {} reported WrongPartition but didn't know the right one!", &replica.group_id);
}
if let Some(pending_request) = maybe_pending_request {
self.send_transaction_request(pending_request);
}
}
Some(transaction_reply::Data::ServiceIdMismatch(_service_id_mismatch_data)) => {
let maybe_pending_request: Option<PendingRequest> =
partition.remote_group.handle_reply(&PendingRequestId { id: transaction_reply.request_id });
if let Some(pending_request) = maybe_pending_request {
warn!("partition {} reported ServiceIdMismatch");
self.cancel_pending_request(pending_request.from);
}
}
Some(transaction_reply::Data::XferInProgress(_xfer_in_progress_data)) => {
let maybe_pending_request: Option<PendingRequest> =
partition.remote_group.handle_reply(&PendingRequestId { id: transaction_reply.request_id });
if let Some(pending_request) = maybe_pending_request {
info!("partition {} reported XferInProgress for backup id {}",
&replica.group_id, OptionDisplay(pending_request.backup_id()));
self.cancel_pending_request(pending_request.from);
}
}
None => {
let maybe_pending_request: Option<PendingRequest> =
partition.remote_group.handle_reply(&PendingRequestId { id: transaction_reply.request_id });
if let Some(pending_request) = maybe_pending_request {
self.cancel_pending_request(pending_request.from);
}
}
}
}
}
fn handle_enclave_get_quote_reply(&mut self, reply: EnclaveGetQuoteReply, from_node_id: NodeId) {
self.replicas.request_attestation(reply.sgx_quote, from_node_id);
}
//
// client requests
//
pub fn client_request(&mut self, data: transaction_request::Data, from: SgxsdMsgFrom) {
self.request_transaction(data, PendingRequestFrom::Client(PendingClientRequest { from }));
}
fn request_transaction(&mut self, data: transaction_request::Data, from: PendingRequestFrom) {
let id = self.last_request_id.clone() + 1;
self.last_request_id = id.clone();
let min_attestation = match &data {
transaction_request::Data::Create(_) |
transaction_request::Data::Delete(_) => {
None
}
transaction_request::Data::Backup(BackupTransactionRequest { valid_from, .. }) |
transaction_request::Data::Restore(RestoreTransactionRequest { valid_from, .. }) => {
Some(AttestationParameters::new(Duration::from_secs(*valid_from)))
}
};
let message = Rc::new(FrontendToReplicaMessage {
inner: Some(frontend_to_replica_message::Inner::TransactionRequest(TransactionRequest {
request_id: id.id,
data: Some(data),
})),
});
let pending_request = PendingRequest {
id,
message,
min_attestation,
from,
};
self.send_transaction_request(pending_request);
}
fn send_transaction_request(&mut self, mut pending_request: PendingRequest) {
let Self { partitions, .. } = self;
if pending_request.id != self.last_request_id {
pending_request.id = self.last_request_id.clone() + 1;
self.last_request_id = pending_request.id.clone();
match &mut Rc::make_mut(&mut pending_request.message).inner {
Some(frontend_to_replica_message::Inner::TransactionRequest(txn_request)) => {
txn_request.request_id = pending_request.id.id;
}
Some(frontend_to_replica_message::Inner::EnclaveGetQuoteRequest(_)) |
None => (),
}
}
if let Some(backup_id) = pending_request.backup_id() {
let maybe_group_id: Option<&RaftGroupId> = self.key_ranges.find(backup_id);
let maybe_partition: Option<&mut Partition> = maybe_group_id.and_then(|group_id| partitions.get_mut(group_id));
if let Some(partition) = maybe_partition {
let trimmed = partition.remote_group.trim_to(self.config.pending_request_count.saturating_sub(1).to_usize(),
self.config.pending_request_ttl);
if trimmed.len() != 0 {
info!("dropping {} old pending requests for partition {}", trimmed.len(), partition.remote_group.name());
}
if partition.remote_group.pending_len() < self.config.pending_request_count.to_usize() {
match partition.remote_group.send(pending_request) {
Ok(()) => (),
Err(RemoteGroupSendError::NotYetValid(pending_request)) => {
reject_pending_request_not_yet_valid(pending_request);
}
Err(RemoteGroupSendError::AlreadySent(pending_request)) => {
warn!("dropping already sent request {}", &pending_request.id.id);
self.cancel_pending_request(pending_request.from);
}
}
} else {
self.cancel_pending_request(pending_request.from);
}
for trimmed_pending_request in trimmed {
self.cancel_pending_request(trimmed_pending_request.from)
}
} else {
warn!("dropping request for missing partition of {}", backup_id);
self.cancel_pending_request(pending_request.from);
}
} else {
self.cancel_pending_request(pending_request.from);
}
}
fn cancel_pending_request(&mut self, from: PendingRequestFrom) {
match from {
PendingRequestFrom::Untrusted { untrusted_request_id } => {
kbupd_send(EnclaveMessage {
inner: Some(enclave_message::Inner::UntrustedTransactionReply(UntrustedTransactionReply {
request_id: untrusted_request_id,
data: None,
})),
});
}
PendingRequestFrom::Client(pending_client_request) => {
drop(pending_client_request);
}
}
}
fn get_partition_replica_mut<'a, 'b>(replicas: &'a mut PeerManager<Replica>,
partitions: &'b mut HashMap<RaftGroupId, Partition, DefaultHasher>,
node_id: &NodeId)
-> Option<(&'a mut Replica, &'b mut Partition)> {
let replica = replicas.get_peer_mut(node_id)?;
let partition = partitions.get_mut(&replica.group_id)?;
Some((replica, partition))
}
pub fn decode_request(request_type: u32, backup_id: Vec<u8>, request_data: &[u8]) -> Result<transaction_request::Data, ()> {
let request = kbupd_client::Request::decode(request_data).map_err(|_| ())?;
let backup_id = BackupId::try_from_slice(&backup_id)?;
match request {
kbupd_client::Request {
backup: Some(backup_request),
restore: None,
delete: None,
} => {
match request_type {
KBUPD_REQUEST_TYPE_ANY |
KBUPD_REQUEST_TYPE_BACKUP => {
Self::validate_backup_request(backup_id, backup_request)
}
_ => Err(()),
}
}
kbupd_client::Request {
backup: None,
restore: Some(restore_request),
delete: None,
} => {
match request_type {
KBUPD_REQUEST_TYPE_ANY |
KBUPD_REQUEST_TYPE_RESTORE => {
Self::validate_restore_request(backup_id, restore_request)
}
_ => Err(()),
}
}
kbupd_client::Request {
backup: None,
restore: None,
delete: Some(delete_request),
} => {
match request_type {
KBUPD_REQUEST_TYPE_ANY |
KBUPD_REQUEST_TYPE_DELETE => {
Self::validate_delete_request(backup_id, delete_request)
}
_ => Err(()),
}
}
_ => Err(()),
}
}
fn validate_backup_request(backup_id: BackupId, mut request: kbupd_client::BackupRequest) -> Result<transaction_request::Data, ()> {
if let kbupd_client::BackupRequest {
service_id,
backup_id: Some(request_backup_id),
nonce: Some(nonce),
valid_from: Some(valid_from),
data: Some(data),
pin: Some(pin),
tries: Some(tries),
} = &mut request {
if (Self::validate_request_service_id(service_id) &&
request_backup_id == &backup_id.id &&
nonce.len() == 32 &&
data.len() == 32 &&
pin.len() == 32 &&
*tries != 0 && *tries <= u16::max_value().into())
{
Ok(transaction_request::Data::Backup(BackupTransactionRequest {
service_id: service_id.take(),
backup_id,
nonce: std::mem::replace(nonce, Vec::new()),
valid_from: *valid_from,
data: SecretBytes { data: std::mem::replace(data, Vec::new()) },
pin: SecretBytes { data: std::mem::replace(pin, Vec::new()) },
tries: *tries,
}))
} else {
Err(())
}
} else {
Err(())
}
}
fn validate_restore_request(backup_id: BackupId, mut request: kbupd_client::RestoreRequest) -> Result<transaction_request::Data, ()> {
if let kbupd_client::RestoreRequest {
service_id,
backup_id: Some(request_backup_id),
nonce: Some(nonce),
valid_from: Some(valid_from),
pin: Some(pin),
} = &mut request {
if (Self::validate_request_service_id(service_id) &&
request_backup_id == &backup_id.id &&
nonce.len() == 32 &&
pin.len() == 32)
{
Ok(transaction_request::Data::Restore(RestoreTransactionRequest {
service_id: service_id.take(),
backup_id,
valid_from: *valid_from,
nonce: std::mem::replace(nonce, Vec::new()),
pin: SecretBytes { data: std::mem::replace(pin, Vec::new()), },
}))
} else {
Err(())
}
} else {
Err(())
}
}
fn validate_delete_request(backup_id: BackupId, request: kbupd_client::DeleteRequest) -> Result<transaction_request::Data, ()> {
if let kbupd_client::DeleteRequest {
service_id,
backup_id: Some(request_backup_id),
} = request {
if (Self::validate_request_service_id(&service_id) &&
request_backup_id == backup_id.id)
{
Ok(transaction_request::Data::Delete(DeleteTransactionRequest {
service_id,
backup_id,
}))
} else {
Err(())
}
} else {
Err(())
}
}
fn validate_request_service_id(maybe_service_id: &Option<Vec<u8>>) -> bool {
if let Some(service_id) = maybe_service_id {
service_id.len() == 32
} else {
true
}
}
}
fn reject_pending_request_not_yet_valid(pending_request: PendingRequest) {
match pending_request.from {
PendingRequestFrom::Client(pending_client_request) => {
info!("rejecting not yet valid client request {} requiring {}",
&pending_request.id.id, OptionDisplay(pending_request.min_attestation.as_ref()));
match &pending_request.message.as_ref().inner {
Some(frontend_to_replica_message::Inner::TransactionRequest(TransactionRequest { data, .. })) => {
match data {
Some(transaction_request::Data::Backup(_)) => {
pending_client_request.reply(&kbupd_client::Response {
backup: Some(kbupd_client::BackupResponse {
status: Some(kbupd_client::backup_response::Status::NotYetValid.into()),
nonce: None,
}),
restore: None,
delete: None,
});
}
Some(transaction_request::Data::Restore(_)) => {
pending_client_request.reply(&kbupd_client::Response {
backup: None,
restore: Some(kbupd_client::RestoreResponse {
status: Some(kbupd_client::restore_response::Status::NotYetValid.into()),
nonce: None,
data: None,
tries: None,
}),
delete: None,
});
}
Some(transaction_request::Data::Create(_)) |
Some(transaction_request::Data::Delete(_)) |
None => (),
}
}
Some(frontend_to_replica_message::Inner::EnclaveGetQuoteRequest(_)) |
None => (),
}
}
PendingRequestFrom::Untrusted { untrusted_request_id } => {
info!("rejecting not yet valid untrusted request {} requiring {}",
untrusted_request_id, OptionDisplay(pending_request.min_attestation.as_ref()));
}
}
}
//
// PartitionKeyRanges impls
//
impl PartitionKeyRanges {
fn range_cmp(one: &PartitionKeyRange, two: &PartitionKeyRange) -> Ordering {
(one.first(), one.last()).cmp(&(two.first(), two.last()))
}
fn entry_cmp(one: &(PartitionKeyRange, RaftGroupId), two: &(PartitionKeyRange, RaftGroupId)) -> Ordering {
Self::range_cmp(&one.0, &two.0)
}
fn key_cmp(range: &PartitionKeyRange, key: &BackupId) -> Ordering {
match range.first().as_ref().cmp(&key.id) {
Ordering::Greater => Ordering::Greater,
Ordering::Less |
Ordering::Equal => {
match range.last().as_ref().cmp(&key.id) {
Ordering::Less => Ordering::Less,
Ordering::Greater |
Ordering::Equal => Ordering::Equal,
}
}
}
}
fn update(&mut self, update_group_id: &RaftGroupId, update_range: PartitionKeyRange) {
let mut matches =
self.ranges.iter_mut()
.filter(|(_, group_id)| group_id == update_group_id)
.peekable();
if matches.peek().is_none() {
match self.ranges[..].binary_search_by(|(range, _)| Self::range_cmp(range, &update_range))
.and_then(|ranges_index| self.ranges.get_mut(ranges_index).ok_or(ranges_index))
{
Ok((_, group_id)) => {
*group_id = update_group_id.clone();
}
Err(ranges_index) => {
self.ranges.insert(ranges_index, (update_range, update_group_id.clone()));
}
}
} else {
for (range, _) in matches {
*range = update_range;
}
}
self.ranges[..].sort_unstable_by(Self::entry_cmp);
}
fn remove(&mut self, remove_group_id: &RaftGroupId) {
self.ranges.retain(|(_range, group_id)| group_id != remove_group_id);
}
fn find<'a>(&'a self, key: &BackupId) -> Option<&'a RaftGroupId> {
self.ranges[..]
.binary_search_by(|(range, _)| Self::key_cmp(range, key))
.ok()
.and_then(|ranges_index| self.ranges.get(ranges_index))
.filter(|(range, _group_id)| range.contains_id(key))
.map(|(_range, group_id)| group_id)
}
}
//
// PendingRequestId impls
//
impl Add<u64> for PendingRequestId {
type Output = Self;
fn add(self, inc: u64) -> Self {
Self { id: self.id.checked_add(inc).unwrap_or_else(|| panic!("overflow")) }
}
}
//
// PendingRequest impls
//
impl PendingRequest {
fn backup_id(&self) -> Option<&BackupId> {
if let Some(frontend_to_replica_message::Inner::TransactionRequest(TransactionRequest { data: Some(data), .. })) = &self.message.inner {
match data {
transaction_request::Data::Create(create_request) => Some(&create_request.backup_id),
transaction_request::Data::Backup(backup_request) => Some(&backup_request.backup_id),
transaction_request::Data::Restore(restore_request) => Some(&restore_request.backup_id),
transaction_request::Data::Delete(delete_request) => Some(&delete_request.backup_id),
}
} else {
None
}
}
}
impl RemoteGroupPendingRequest for PendingRequest {
type RequestId = PendingRequestId;
type Message = FrontendToReplicaMessage;
fn request_id(&self) -> &Self::RequestId {
&self.id
}
fn message(&self) -> Rc<Self::Message> {
Rc::clone(&self.message)
}
fn min_attestation(&self) -> Option<AttestationParameters> {
self.min_attestation
}
}
//
// PendingClientRequest impls
//
impl PendingClientRequest {
pub fn reply(self, reply: &kbupd_client::Response) {
let mut data = SecretValue::new(Vec::with_capacity(reply.encoded_len()));
match reply.encode(data.get_mut()) {
Ok(()) => (),
Err(_) => {
error!("error encoding client reply");
return;
}
}
match self.from.reply(data.get_mut()) {
Ok(()) => {
// no need to erase, as SgxsdFrom::reply encrypts in-place
data.get_mut().clear();
}
Err(sgx_status) => {
error!("error replying to client request: {}", sgx_status);
}
}
}
}
//
// Replica impls
//
impl Peer for Replica {
type Message = ReplicaToFrontendMessage;
fn remote_mut(&mut self) -> &mut dyn Remote {
&mut self.remote
}
fn recv(&mut self, msg_data: &[u8]) -> Result<Self::Message, RemoteRecvError> {
self.remote.recv(msg_data)
}
fn send_quote_reply(&mut self, _reply: EnclaveGetQuoteReply) -> Result<(), ()> {
Ok(())
}
}
//
// RemoteSender impls
//
impl RemoteGroupNode for RemoteSender<FrontendToReplicaMessage> {
fn request_quote(&mut self, request: EnclaveGetQuoteRequest) -> Result<(), ()> {
self.send(Rc::new(FrontendToReplicaMessage {
inner: Some(frontend_to_replica_message::Inner::EnclaveGetQuoteRequest(request)),
}))
}
}
//
// internal
//
fn validate_untrusted_transaction_request(request_data: Option<untrusted_transaction_request::Data>)
-> Result<transaction_request::Data, ()> {
match request_data {
Some(untrusted_transaction_request::Data::CreateBackupRequest(create_backup_request)) => {
if create_backup_request.backup_id.id.len() == 32 {
Ok(transaction_request::Data::Create(create_backup_request))
} else {
Err(())
}
}
Some(untrusted_transaction_request::Data::DeleteBackupRequest(delete_backup_request)) => {
if delete_backup_request.backup_id.id.len() == 32 {
Ok(transaction_request::Data::Delete(DeleteTransactionRequest {
service_id: None,
backup_id: delete_backup_request.backup_id,
}))
} else {
Err(())
}
}
None => Err(()),
}
}

View File

@ -0,0 +1,145 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use std::cell::*;
use sgx_ffi::sgx::*;
use sgxsd_ffi::ecalls::*;
use crate::ffi::ecalls::*;
use crate::protobufs::kbupd::*;
use crate::protobufs::kbupd::untrusted_message;
use crate::service::frontend::*;
use crate::service::replica::*;
//
// public api
//
#[allow(variant_size_differences)]
pub enum ServiceState {
NotStarted,
Frontend(FrontendState),
Replica(ReplicaState),
}
pub struct SgxsdState {
}
#[cfg(not(any(test, feature = "test")))]
pub fn whereis<F, R>(fun: F) -> R
where F: FnOnce(&RefCell<ServiceState>) -> R
{
#[thread_local]
static SERVICE: RefCell<ServiceState> = RefCell::new(ServiceState::NotStarted);
fun(&SERVICE)
}
#[cfg(any(test, feature = "test"))]
pub fn whereis<F, R>(fun: F) -> R
where F: FnOnce(&RefCell<ServiceState>) -> R
{
thread_local! {
static SERVICE: RefCell<ServiceState> = RefCell::new(ServiceState::NotStarted);
}
SERVICE.with(fun)
}
//
// ServiceState impls
//
impl KbupdService for ServiceState {
fn untrusted_message(&mut self, msg: UntrustedMessage) {
match msg.inner {
Some(untrusted_message::Inner::StartFrontendRequest(start_frontend_req)) => {
if let ServiceState::NotStarted = self {
*self = ServiceState::Frontend(FrontendState::init(start_frontend_req));
} else {
warn!("node service already started");
}
}
Some(untrusted_message::Inner::StartReplicaRequest(start_replica_req)) => {
if let ServiceState::NotStarted = self {
*self = ServiceState::Replica(ReplicaState::init(start_replica_req));
} else {
warn!("node service already started");
}
}
Some(_) => {
match self {
ServiceState::Replica(replica) => {
replica.untrusted_message(msg);
}
ServiceState::Frontend(frontend) => {
frontend.untrusted_message(msg);
}
ServiceState::NotStarted => {
warn!("node service not started");
}
}
}
None => {
}
}
}
}
impl SgxsdServer for SgxsdState {
type InitArgs = StartArgs;
type HandleCallArgs = CallArgs;
type TerminateArgs = StopArgs;
fn init(_args: Option<&Self::InitArgs>) -> Result<Self, SgxStatus> {
Ok(Self {})
}
fn handle_call(&mut self,
args: Option<&Self::HandleCallArgs>,
request_data: &[u8],
from: SgxsdMsgFrom)
-> Result<(), (SgxStatus, SgxsdMsgFrom)>
{
let args = match args {
Some(args) => args,
None => return Err((SGX_ERROR_INVALID_PARAMETER, from)),
};
match FrontendState::decode_request(args.request_type, args.backup_id.to_vec(), request_data) {
Ok(request) => {
whereis(|service_ref| {
let mut service = service_ref.borrow_mut();
if let ServiceState::Frontend(frontend) = &mut *service {
frontend.client_request(request, from);
Ok(())
} else {
warn!("frontend service not started");
Err((SGX_ERROR_INVALID_STATE, from))
}
})
}
Err(()) => {
Err((SGX_ERROR_INVALID_PARAMETER, from))
}
}
}
fn terminate(self, _args: Option<&Self::TerminateArgs>) -> Result<(), SgxStatus> {
Ok(())
}
}

View File

@ -0,0 +1,20 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
pub mod main;
pub mod frontend;
pub mod replica;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,564 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use std::fmt;
use std::ops::*;
use crate::protobufs::kbupd::*;
use crate::util::*;
#[derive(Copy, Clone, Eq, PartialOrd, Ord)]
pub struct PartitionKey([u8; 32]);
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct PartitionKeyRange {
first: PartitionKey,
last: PartitionKey,
}
//
// PartittionKeyRange impls
//
impl PartitionKeyRange {
pub fn new(first: PartitionKey, last: PartitionKey) -> Result<Self, ()> {
if first <= last {
Ok(Self { first, last })
} else {
Err(())
}
}
pub fn new_unbounded() -> Self {
Self {
first: PartitionKey::new([0x00; 32]),
last: PartitionKey::new([0xFF; 32]),
}
}
pub fn try_from_pb(pb: &PartitionKeyRangePb) -> Result<Self, ()> {
Self::from_ids(&pb.first, &pb.last)
}
fn from_ids(first: &BackupId, last: &BackupId) -> Result<Self, ()> {
Self::new(PartitionKey::try_from_pb(first)?, PartitionKey::try_from_pb(last)?)
}
pub fn to_pb(&self) -> PartitionKeyRangePb {
PartitionKeyRangePb {
first: BackupId { id: self.first.to_vec() },
last: BackupId { id: self.last.to_vec() },
}
}
pub fn first(&self) -> &PartitionKey {
&self.first
}
pub fn last(&self) -> &PartitionKey {
&self.last
}
pub fn contains(&self, key: &[u8; 32]) -> bool {
(key >= &self.first &&
key <= &self.last)
}
pub fn contains_id(&self, key: &BackupId) -> bool {
let mut id: [u8; 32] = Default::default();
if key.id.len() == id.len() {
id.copy_from_slice(&key.id);
self.contains(&id)
} else {
false
}
}
pub fn contains_range(&self, other: &Self) -> bool {
self.contains(&other.first) && self.contains(&other.last)
}
pub fn overlaps_range(&self, other: &Self) -> bool {
(self.contains(&other.first) || self.contains(&other.last) ||
other.contains(&self.first) || other.contains(&self.last))
}
pub fn split_off_inclusive(&mut self, new_last: &PartitionKey) -> Result<Option<Self>, ()> {
if self.contains(new_last) {
if let Some(other_first) = new_last.checked_add(1) {
let other_last = std::mem::replace(&mut self.last, *new_last);
Ok(Self::new(other_first, other_last).ok())
} else {
Ok(None)
}
} else {
Err(())
}
}
}
impl RangeBounds<PartitionKey> for &PartitionKeyRange {
fn start_bound(&self) -> Bound<&PartitionKey> {
Bound::Included(&self.first)
}
fn end_bound(&self) -> Bound<&PartitionKey> {
Bound::Included(&self.last)
}
}
impl fmt::Debug for PartitionKeyRange {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt::Display::fmt(self, fmt)
}
}
impl fmt::Display for PartitionKeyRange {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
let Self { first, last } = self;
write!(fmt, "{}-{}", first, last)
}
}
//
// PartitionKey impls
//
impl PartitionKey {
pub fn new(value: [u8; 32]) -> Self {
Self(value)
}
pub fn try_from_pb(backup_id: &BackupId) -> Result<Self, ()> {
let mut value = [0u8; 32];
if backup_id.id.len() == value.len() {
value.copy_from_slice(&backup_id.id);
Ok(Self(value))
} else {
Err(())
}
}
pub fn to_pb(&self) -> BackupId {
BackupId {
id: self.0.to_vec(),
}
}
#[allow(clippy::indexing_slicing, clippy::integer_arithmetic)]
pub fn checked_sub(&self, rhs: u8) -> Option<Self> {
let mut ret = [0x00; 32];
let mut carry = rhs;
for (i, b) in self.0.iter().rev().enumerate() {
let new_b = b.overflowing_sub(carry);
carry = if new_b.1 { 1 } else { 0 };
ret[31 - i] = new_b.0;
}
if carry == 0 {
Some(Self(ret))
} else {
None
}
}
#[allow(clippy::indexing_slicing, clippy::integer_arithmetic)]
pub fn checked_add(&self, rhs: u8) -> Option<Self> {
let mut ret = [0x00; 32];
let mut carry = rhs;
for (i, b) in self.0.iter().rev().enumerate() {
let new_b = b.overflowing_add(carry);
carry = if new_b.1 { 1 } else { 0 };
ret[31 - i] = new_b.0;
}
if carry == 0 {
Some(Self(ret))
} else {
None
}
}
}
impl Deref for PartitionKey {
type Target = [u8; 32];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> PartialEq<T> for PartitionKey
where T: AsRef<[u8]>
{
fn eq(&self, other: &T) -> bool {
self.0 == other.as_ref()
}
}
impl AsRef<[u8]> for PartitionKey {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl fmt::Debug for PartitionKey {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
fmt::Display::fmt(self, fmt)
}
}
impl fmt::Display for PartitionKey {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
let Self(key) = self;
write!(fmt, "{}", ToHex(key))
}
}
//
// tests
//
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn valid_full_range() {
PartitionKeyRange::try_from_pb(
&PartitionKeyRange::from_ids(&BackupId { id: vec![0x00; 32] },
&BackupId { id: vec![0xFF; 32] })
.unwrap()
.to_pb()
).unwrap();
}
#[test]
fn valid_empty_range() {
PartitionKeyRange::try_from_pb(
&PartitionKeyRange::from_ids(&BackupId { id: vec![0x00; 32] },
&BackupId { id: vec![0x00; 32] })
.unwrap()
.to_pb()
).unwrap();
}
#[test]
fn invalid_inverted_range() {
PartitionKeyRange::from_ids(&BackupId { id: vec![0xFF; 32] },
&BackupId { id: vec![0x00; 32] })
.unwrap_err();
}
#[test]
fn invalid_range_empty_first() {
PartitionKeyRange::from_ids(&BackupId { id: vec![0x00; 0] },
&BackupId { id: vec![0xFF; 32] })
.unwrap_err();
}
#[test]
fn invalid_range_empty_last() {
PartitionKeyRange::from_ids(&BackupId { id: vec![0x00; 32] },
&BackupId { id: vec![0xFF; 0] })
.unwrap_err();
}
#[test]
fn invalid_range_short_first() {
PartitionKeyRange::from_ids(&BackupId { id: vec![0x00; 31] },
&BackupId { id: vec![0xFF; 32] })
.unwrap_err();
}
#[test]
fn invalid_range_short_last() {
PartitionKeyRange::from_ids(&BackupId { id: vec![0x00; 31] },
&BackupId { id: vec![0xFF; 32] })
.unwrap_err();
}
#[test]
fn invalid_range_long_first() {
PartitionKeyRange::from_ids(&BackupId { id: vec![0x00; 33] },
&BackupId { id: vec![0xFF; 32] })
.unwrap_err();
}
#[test]
fn invalid_range_long_last() {
PartitionKeyRange::from_ids(&BackupId { id: vec![0x00; 32] },
&BackupId { id: vec![0xFF; 33] })
.unwrap_err();
}
#[test]
fn contains_id_valid() {
let range = PartitionKeyRange::new_unbounded();
assert!(range.contains_id(&BackupId { id: vec![0x00; 32] }));
assert!(range.contains_id(&BackupId { id: vec![0xFF; 32] }));
assert!(range.contains_id(&BackupId { id: vec![0x80; 32] }));
let range = PartitionKeyRange::new(PartitionKey::new([0x00; 32]), PartitionKey::new([0x80; 32])).unwrap();
assert!(range.contains_id(&BackupId { id: vec![0x00; 32] }));
assert!(range.contains_id(&PartitionKey::new([0x80; 32]).checked_sub(1).unwrap().to_pb()));
assert!(range.contains_id(&BackupId { id: vec![0x80; 32] }));
assert!(!range.contains_id(&PartitionKey::new([0x80; 32]).checked_add(1).unwrap().to_pb()));
assert!(!range.contains_id(&BackupId { id: vec![0xFF; 32] }));
let range = PartitionKeyRange::new(PartitionKey::new([0xBE; 32]), PartitionKey::new([0xEF; 32])).unwrap();
assert!(!range.contains_id(&BackupId { id: vec![0x00; 32] }));
assert!(!range.contains_id(&PartitionKey::new([0xBE; 32]).checked_sub(1).unwrap().to_pb()));
assert!(range.contains_id(&BackupId { id: vec![0xBE; 32] }));
assert!(range.contains_id(&PartitionKey::new([0xBE; 32]).checked_add(1).unwrap().to_pb()));
assert!(range.contains_id(&PartitionKey::new([0xEF; 32]).checked_sub(1).unwrap().to_pb()));
assert!(range.contains_id(&BackupId { id: vec![0xEF; 32] }));
assert!(!range.contains_id(&PartitionKey::new([0xEF; 32]).checked_add(1).unwrap().to_pb()));
assert!(!range.contains_id(&BackupId { id: vec![0xFF; 32] }));
}
#[test]
fn contains_id_invalid() {
assert!(!PartitionKeyRange::new_unbounded().contains_id(&BackupId { id: vec![] }));
assert!(!PartitionKeyRange::new_unbounded().contains_id(&BackupId { id: vec![0x00; 31] }));
assert!(!PartitionKeyRange::new_unbounded().contains_id(&BackupId { id: vec![0x00; 33] }));
}
macro_rules! key {
([$value:expr] + $addend:expr) => ({
PartitionKey::new([$value; 32]).checked_add($addend)
.unwrap_or_else(|| panic!("test key overflow"))
});
([$value:expr] - $subtrahend:expr) => ({
PartitionKey::new([$value; 32]).checked_sub($subtrahend)
.unwrap_or_else(|| panic!("test key underflow"))
});
([$value:expr]) => ({
PartitionKey::new([$value; 32])
});
}
macro_rules! range {
([$value:expr], $($second:tt)*) => (range!({key!([$value])}, $($second)*));
([$value:expr] + $addend:expr, $($second:tt)*) => (range!({key!([$value] + $addend)}, $($second)*));
([$value:expr] - $subtrahend:expr, $($second:tt)*) => (range!({key!([$value] - $subtrahend)}, $($second)*));
($first:block, [$value:expr]) => (range!($first, {key!([$value])}));
($first:block, [$value:expr] + $addend:expr) => (range!($first, {key!([$value] + $addend)}));
($first:block, [$value:expr] - $subtrahend:expr) => (range!($first, {key!([$value] - $subtrahend)}));
($first:block, $last:block) => ({
PartitionKeyRange::new($first, $last).unwrap_or_else(|()| panic!("invalid test range"))
});
() => ({
PartitionKeyRange::new_unbounded()
});
}
#[test]
fn overlaps_range() {
let range = range!();
assert!(range.overlaps_range(&range!()));
assert!(range.overlaps_range(&range!([0x00], [0x00])));
assert!(range.overlaps_range(&range!([0x00], [0xBE])));
assert!(range.overlaps_range(&range!([0xBE], [0xEF])));
assert!(range.overlaps_range(&range!([0xEF], [0xFF])));
assert!(range.overlaps_range(&range!([0xFF], [0xFF])));
let range = range!([0x00], [0xBE]);
assert!( range.overlaps_range(&range!()));
assert!( range.overlaps_range(&range!([0x00], [0x00])));
assert!( range.overlaps_range(&range!([0x00], [0xBE]-1)));
assert!( range.overlaps_range(&range!([0x00], [0xBE])));
assert!( range.overlaps_range(&range!([0x00], [0xFF])));
assert!( range.overlaps_range(&range!([0xBA], [0xBA])));
assert!( range.overlaps_range(&range!([0xBA], [0xBE])));
assert!( range.overlaps_range(&range!([0xBA], [0xBE]+1)));
assert!( range.overlaps_range(&range!([0xBE], [0xBE])));
assert!( range.overlaps_range(&range!([0xBE], [0xBE]+1)));
assert!(!range.overlaps_range(&range!([0xBE]+1, [0xEF])));
assert!(!range.overlaps_range(&range!([0xEF], [0xFF])));
assert!(!range.overlaps_range(&range!([0xFF], [0xFF])));
let range = range!([0xBE], [0xEF]);
assert!( range.overlaps_range(&range!()));
assert!(!range.overlaps_range(&range!([0x00], [0x00])));
assert!(!range.overlaps_range(&range!([0x00], [0xBE]-1)));
assert!( range.overlaps_range(&range!([0x00], [0xBE])));
assert!( range.overlaps_range(&range!([0x00], [0xBF])));
assert!( range.overlaps_range(&range!([0x00], [0xEF])));
assert!( range.overlaps_range(&range!([0x00], [0xEF]+1)));
assert!( range.overlaps_range(&range!([0x00], [0xFF])));
assert!( range.overlaps_range(&range!([0xBE], [0xEF])));
assert!( range.overlaps_range(&range!([0xBE], [0xEF]+1)));
assert!( range.overlaps_range(&range!([0xBE]+1, [0xEF]-1)));
assert!( range.overlaps_range(&range!([0xBE]+1, [0xEF])));
assert!( range.overlaps_range(&range!([0xBE]+1, [0xEF]+1)));
assert!( range.overlaps_range(&range!([0xEF], [0xEF])));
assert!( range.overlaps_range(&range!([0xEF], [0xEF]+1)));
assert!(!range.overlaps_range(&range!([0xEF]+1, [0xEF]+1)));
assert!(!range.overlaps_range(&range!([0xEF]+1, [0xFF])));
let range = range!([0xEF], [0xFF]);
assert!(!range.overlaps_range(&range!([0x00], [0x00])));
assert!(!range.overlaps_range(&range!([0x00], [0xEF]-1)));
assert!( range.overlaps_range(&range!([0x00], [0xEF])));
assert!( range.overlaps_range(&range!([0x00], [0xEF]+1)));
assert!( range.overlaps_range(&range!([0x00], [0xFF])));
assert!( range.overlaps_range(&range!([0xEF], [0xEF])));
assert!( range.overlaps_range(&range!([0xEF], [0xEF]+1)));
assert!( range.overlaps_range(&range!([0xEF], [0xFF])));
assert!( range.overlaps_range(&range!([0xEF]+1, [0xFF]-1)));
assert!( range.overlaps_range(&range!([0xEF]+1, [0xFF])));
assert!( range.overlaps_range(&range!([0xFF], [0xFF])));
}
#[test]
fn split_off_first() {
let mut range = PartitionKeyRange::new_unbounded();
let split_off = range.split_off_inclusive(&PartitionKey::new([0x00; 32])).unwrap().unwrap();
assert_eq!(range.first(), &[0x00; 32]);
assert_eq!(range.last(), &[0x00; 32]);
assert_eq!(split_off.first(), &range.last().checked_add(1).unwrap());
assert_eq!(split_off.last(), &[0xff; 32]);
let mut range = PartitionKeyRange::new(PartitionKey::new([0xbe; 32]), PartitionKey::new([0xef; 32])).unwrap();
let split_off = range.split_off_inclusive(&PartitionKey::new([0xbe; 32])).unwrap().unwrap();
assert_eq!(range.first(), &[0xbe; 32]);
assert_eq!(range.last(), &[0xbe; 32]);
assert_eq!(split_off.first(), &range.last().checked_add(1).unwrap());
assert_eq!(split_off.last(), &[0xef; 32]);
}
#[test]
fn split_off_mid() {
let mut range = PartitionKeyRange::new_unbounded();
let split_off = range.split_off_inclusive(&PartitionKey::new([0x80; 32])).unwrap().unwrap();
assert_eq!(range.first(), &[0x00; 32]);
assert_eq!(range.last(), &[0x80; 32]);
assert_eq!(split_off.first(), &range.last().checked_add(1).unwrap());
assert_eq!(split_off.last(), &[0xff; 32]);
let mut range = PartitionKeyRange::new(PartitionKey::new([0xbe; 32]), PartitionKey::new([0xef; 32])).unwrap();
let split_off = range.split_off_inclusive(&PartitionKey::new([0xdd; 32])).unwrap().unwrap();
assert_eq!(range.first(), &[0xbe; 32]);
assert_eq!(range.last(), &[0xdd; 32]);
assert_eq!(split_off.first(), &range.last().checked_add(1).unwrap());
assert_eq!(split_off.last(), &[0xef; 32]);
}
#[test]
fn split_off_last() {
let mut range = PartitionKeyRange::new_unbounded();
let split_off = range.split_off_inclusive(&PartitionKey::new([0xff; 32])).unwrap();
assert_eq!(range.first(), &[0x00; 32]);
assert_eq!(range.last(), &[0xff; 32]);
assert!(split_off.is_none());
let mut range = PartitionKeyRange::new(PartitionKey::new([0xbe; 32]), PartitionKey::new([0xef; 32])).unwrap();
let split_off = range.split_off_inclusive(&PartitionKey::new([0xef; 32])).unwrap();
assert_eq!(range.first(), &[0xbe; 32]);
assert_eq!(range.last(), &[0xef; 32]);
assert!(split_off.is_none());
}
#[test]
fn split_off_small() {
let mut range = PartitionKeyRange::new(PartitionKey::new([0x00; 32]), PartitionKey::new([0x00; 32])).unwrap();
let split_off = range.split_off_inclusive(&PartitionKey::new([0x00; 32])).unwrap();
assert_eq!(range.first(), &[0x00; 32]);
assert_eq!(range.last(), &[0x00; 32]);
assert!(split_off.is_none());
let mut range = PartitionKeyRange::new(PartitionKey::new([0xbe; 32]), PartitionKey::new([0xbe; 32])).unwrap();
let split_off = range.split_off_inclusive(&PartitionKey::new([0xbe; 32])).unwrap();
assert_eq!(range.first(), &[0xbe; 32]);
assert_eq!(range.last(), &[0xbe; 32]);
assert!(split_off.is_none());
let mut range = PartitionKeyRange::new(PartitionKey::new([0xff; 32]), PartitionKey::new([0xff; 32])).unwrap();
let split_off = range.split_off_inclusive(&PartitionKey::new([0xff; 32])).unwrap();
assert_eq!(range.first(), &[0xff; 32]);
assert_eq!(range.last(), &[0xff; 32]);
assert!(split_off.is_none());
}
#[test]
fn split_off_err() {
let mut range = PartitionKeyRange::new(PartitionKey::new([0x00; 32]), PartitionKey::new([0x00; 32])).unwrap();
range.split_off_inclusive(&range.last().checked_add(1).unwrap()).unwrap_err();
range.split_off_inclusive(&PartitionKey::new([0xff; 32])).unwrap_err();
let mut range = PartitionKeyRange::new(PartitionKey::new([0x00; 32]), PartitionKey::new([0xbe; 32])).unwrap();
range.split_off_inclusive(&range.last().checked_add(1).unwrap()).unwrap_err();
range.split_off_inclusive(&PartitionKey::new([0xbf; 32])).unwrap_err();
range.split_off_inclusive(&PartitionKey::new([0xff; 32])).unwrap_err();
let mut range = PartitionKeyRange::new(PartitionKey::new([0xbe; 32]), PartitionKey::new([0xbe; 32])).unwrap();
range.split_off_inclusive(&PartitionKey::new([0x00; 32])).unwrap_err();
range.split_off_inclusive(&PartitionKey::new([0xba; 32])).unwrap_err();
range.split_off_inclusive(&range.first().checked_sub(1).unwrap()).unwrap_err();
range.split_off_inclusive(&range.last().checked_add(1).unwrap()).unwrap_err();
range.split_off_inclusive(&PartitionKey::new([0xef; 32])).unwrap_err();
range.split_off_inclusive(&PartitionKey::new([0xff; 32])).unwrap_err();
let mut range = PartitionKeyRange::new(PartitionKey::new([0xbf; 32]), PartitionKey::new([0xff; 32])).unwrap();
range.split_off_inclusive(&PartitionKey::new([0x00; 32])).unwrap_err();
range.split_off_inclusive(&PartitionKey::new([0xbe; 32])).unwrap_err();
range.split_off_inclusive(&range.first().checked_sub(1).unwrap()).unwrap_err();
let mut range = PartitionKeyRange::new(PartitionKey::new([0xff; 32]), PartitionKey::new([0xff; 32])).unwrap();
range.split_off_inclusive(&PartitionKey::new([0x00; 32])).unwrap_err();
range.split_off_inclusive(&PartitionKey::new([0xfe; 32])).unwrap_err();
}
#[test]
fn checked_add() {
for index in 0..32 {
for index_value in 0x00..0xfe {
let mut value = [0x00; 32];
for byte in value[index + 1..32].iter_mut() {
*byte = 0xff;
}
value[index] = index_value;
let mut addend = [0x00; 32];
addend[index] = index_value + 1;
assert_eq!(PartitionKey::new(value).checked_add(1).unwrap(), &addend);
}
}
}
#[test]
fn checked_sub() {
for index in 0..32 {
for index_value in 0x01..0xff {
let mut value = [0x00; 32];
value[index] = index_value;
let mut addend = [0x00; 32];
addend[index] = index_value - 1;
for byte in addend[index + 1..32].iter_mut() {
*byte = 0xff;
}
assert_eq!(PartitionKey::new(value).checked_sub(1).unwrap(), &addend);
}
}
}
#[test]
fn checked_add_overflow() {
assert_eq!(PartitionKey::new([0xff; 32]).checked_add(1), None);
}
#[test]
fn checked_sub_overflow() {
assert_eq!(PartitionKey::new([0x00; 32]).checked_sub(1), None);
}
}

View File

@ -0,0 +1,242 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::prelude::*;
use std::fmt;
use std::rc::*;
use std::time::*;
use sgxsd_ffi::{RdRand};
use crate::protobufs::kbupd_enclave::*;
use crate::raft::*;
use crate::remote::*;
use crate::storage::*;
use crate::util::{ListDisplay};
use super::*;
const ATTESTATION_EXPIRATION_WINDOW: Duration = Duration::from_secs(86400);
pub(super) struct ReplicaGroupState {
pub raft: RaftState<RaftLogStorage, RdRand, NodeId>,
remotes: Box<[RemoteReplicaState]>,
attestation_time_ticks: u32,
request_quote_ticks: u32,
attestation_time_now: Duration,
}
pub(super) struct RemoteReplicaState {
pub sender: ReplicaRemoteSender,
}
//
// ReplicaGroupState impls
//
impl ReplicaGroupState {
pub fn new(raft: RaftState<RaftLogStorage, RdRand, NodeId>,
remotes: Box<[RemoteReplicaState]>)
-> Self
{
Self {
raft,
remotes,
attestation_time_ticks: 0,
request_quote_ticks: 0,
attestation_time_now: Duration::from_secs(0),
}
}
pub fn id(&self) -> &RaftGroupId {
self.raft.group_id()
}
pub fn set_config(&mut self, config: &EnclaveReplicaConfig) {
self.raft.log_mut().set_index_cache_size(config.raft_log_index_page_cache_size.to_usize());
self.raft.set_election_timeout_ticks(config.election_timeout_ticks);
self.raft.set_heartbeat_timeout_ticks(config.heartbeat_timeout_ticks);
self.raft.set_replication_chunk_size(config.replication_chunk_size.to_usize());
}
pub fn get(&self, node_id: &NodeId) -> Option<&RemoteReplicaState> {
self.remotes.iter().find(|replica: &&RemoteReplicaState| replica.sender.id() == node_id)
}
pub fn timer_tick(&mut self,
max_attestation_time_ticks: u32,
max_request_quote_ticks: u32,
now: Duration)
-> Option<TransactionData> {
self.attestation_time_ticks = self.attestation_time_ticks.saturating_add(1);
self.request_quote_ticks = self.request_quote_ticks.saturating_add(1);
if self.request_quote_ticks >= max_request_quote_ticks {
self.request_quote_ticks = Default::default();
for remote in &mut self.remotes[..] {
let _ignore = remote.sender.request_quote(EnclaveGetQuoteRequest {});
}
}
if self.attestation_time_ticks >= max_attestation_time_ticks {
if now > self.attestation_time_now {
match self.check_quorum_attestation_time(now) {
Ok(()) => {
self.attestation_time_ticks = 0;
Some(TransactionData {
inner: Some(transaction_data::Inner::SetTime(SetTimeTransaction {
now_secs: now.as_secs(),
})),
})
}
Err(invalid) => {
if self.attestation_time_ticks.checked_rem(max_attestation_time_ticks) == Some(0) {
info!("not setting attestation time to {} with invalid attestations for {} of {} replicas: {}",
now.as_secs(), invalid.len(), self.remotes.iter().len(), ListDisplay(invalid));
}
None
}
}
} else {
if now < self.attestation_time_now {
warn!("not setting attestation time backward from {} to {}",
self.attestation_time_now.as_secs(), now.as_secs());
}
None
}
} else {
None
}
}
pub fn is_authorized(&self, node_id: &NodeId) -> bool {
(self.get(node_id))
.map(|replica: &RemoteReplicaState| replica.check_attestation_time(self.attestation_time_now))
.unwrap_or(false)
}
pub fn set_attestation_time_now(&mut self, now: Duration) -> bool {
if now > self.attestation_time_now {
for replica in self.remotes.iter() {
if !replica.check_attestation_time(now) {
if let Some(attestation) = replica.attestation() {
warn!("replica {} is now invalid at {}: {}",
replica.sender.id(), now.as_secs(), attestation);
}
}
}
info!("set attestation time from {} to {}",
self.attestation_time_now.as_secs(), now.as_secs());
self.attestation_time_now = now;
true
} else {
now < self.attestation_time_now
}
}
pub fn get_attestation_time_now(&self) -> Duration {
self.attestation_time_now
}
pub fn attestation(&self) -> AttestationParameters {
AttestationParameters::new(self.attestation_time_now)
}
pub fn attestation_expiration_window(&self) -> AttestationParameters {
let min_unix_timestamp = self.attestation_time_now.checked_sub(ATTESTATION_EXPIRATION_WINDOW).unwrap_or_default();
AttestationParameters::new(min_unix_timestamp)
}
fn check_quorum_attestation_time(&self, now: Duration) -> Result<(), Vec<&RemoteReplicaState>> {
let replicas_iter = self.remotes.iter();
let replicas_len = replicas_iter.len();
let invalid: Vec<_> = replicas_iter
.filter(|replica: &&RemoteReplicaState| !replica.check_attestation_time(now))
.collect();
if invalid.len() < quorum_size(replicas_len) {
if invalid.is_empty() {
info!("setting attestation time to {}", now.as_secs());
} else {
warn!("setting attestation time to {} with invalid attestations for {} of {} replicas: {}",
now.as_secs(), invalid.len(), replicas_len, ListDisplay(invalid));
}
Ok(())
} else {
Err(invalid)
}
}
pub fn send_raft_message(&self, sendable: SendableRaftMessage<NodeId>) {
match sendable {
SendableRaftMessage::Broadcast { message } => {
let r2r_message = Rc::new(ReplicaToReplicaMessage {
inner: Some(replica_to_replica_message::Inner::RaftMessage(message)),
});
for replica in self.remotes.iter() {
if replica.check_attestation_time(self.attestation_time_now) {
let _ignore = replica.sender.send(Rc::clone(&r2r_message));
} else {
verbose!("dropping broadcast message to {} with expired attestation {}",
replica.sender.id(), OptionDisplay(replica.attestation()));
}
}
}
SendableRaftMessage::Reply { message, from } => {
if let Some(replica) = self.get(&from) {
if replica.check_attestation_time(self.attestation_time_now) {
let r2r_message = Rc::new(ReplicaToReplicaMessage {
inner: Some(replica_to_replica_message::Inner::RaftMessage(message)),
});
let _ignore = replica.sender.send(r2r_message);
} else {
warn!("dropping message to {} with expired attestation {}: {}",
replica.sender.id(), OptionDisplay(replica.attestation()), message);
}
}
}
}
}
}
//
// RemoteReplicaState impls
//
impl RemoteReplicaState {
pub fn attestation(&self) -> Option<AttestationParameters> {
self.sender.attestation()
}
fn check_attestation_time(&self, now: Duration) -> bool {
let min_unix_timestamp = now.checked_sub(ATTESTATION_EXPIRATION_WINDOW).unwrap_or_default();
let min_attestation = AttestationParameters::new(min_unix_timestamp);
self.attestation() >= Some(min_attestation)
}
}
impl fmt::Display for RemoteReplicaState {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { sender } = self;
write!(fmt, "({}, {})", sender, OptionDisplay(sender.attestation()))
}
}

View File

@ -0,0 +1,26 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
pub mod storage_array;
pub mod storage_data;
pub mod storage_page_cache;
pub mod raft_log;
pub use self::storage_array::*;
pub use self::storage_data::*;
pub use self::storage_page_cache::*;
pub use self::raft_log::*;

View File

@ -0,0 +1,131 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
mod raft_log_data;
mod raft_log_index;
use sgx_ffi::util::{SecretValue};
use crate::prelude::*;
use crate::protobufs::raft::*;
use crate::raft::*;
use crate::util::*;
use self::raft_log_data::*;
use self::raft_log_index::*;
pub struct RaftLogStorage {
index: RaftLogIndex,
data: RaftLogData,
cancelled: Vec<SecretValue<Vec<u8>>>,
}
//
// RaftLog impls
//
impl RaftLogStorage {
pub fn new(data_size: usize, index_size: u32, index_cache_size: usize) -> Result<Self, ()> {
Ok(Self {
index: RaftLogIndex::new(index_size, index_cache_size)?,
data: RaftLogData::new(data_size)?,
cancelled: Default::default(),
})
}
pub fn take_cancelled(&mut self) -> Vec<SecretValue<Vec<u8>>> {
std::mem::replace(&mut self.cancelled, Default::default())
}
pub fn data_len(&self) -> usize {
self.data.len()
}
pub fn set_index_cache_size(&mut self, index_cache_size: usize) {
self.index.set_cache_size(index_cache_size);
}
}
impl RaftLog for RaftLogStorage {
fn append(&mut self, log_entry: LogEntry) -> Result<(), RaftLogAppendError> {
let term = log_entry.term;
if !self.index.is_full() {
let data_entry = if !log_entry.data.is_empty() {
let data_entry = self.data.append(log_entry)?;
Some(data_entry)
} else {
None
};
let index_entry = RaftLogIndexEntry { term, data: data_entry };
if let Ok(()) = self.index.push_back(index_entry) {
Ok(())
} else {
Err(RaftLogAppendError::InternalError)
}
} else {
Err(RaftLogAppendError::OutOfSpace { log_entry })
}
}
fn pop_front(&mut self, truncate_to: LogIdx) -> Result<(), ()> {
if (!self.index.is_empty() &&
self.index.prev_log_idx() < truncate_to)
{
if let Some(popped_entry) = self.index.pop_front() {
if let Some(popped_data_entry) = &popped_entry.data {
self.data.pop_front_to(popped_data_entry);
}
}
Ok(())
} else {
Err(())
}
}
fn cancel_from(&mut self, from_log_idx: LogIdx) -> Result<usize, ()> {
let cancelled_data_entries = self.index.cancel_from(from_log_idx)?;
self.cancelled = self.data.cancel(cancelled_data_entries);
Ok(self.cancelled.len())
}
fn get(&mut self, log_idx: LogIdx) -> Option<LogEntry> {
let index_entry = self.index.get(log_idx)?;
let entry_data = if let Some(data_entry) = &index_entry.data {
self.data.read(data_entry)?
} else {
SecretValue::new(Vec::new())
};
Some(LogEntry::new(index_entry.term, entry_data))
}
fn get_len(&mut self, log_idx: LogIdx) -> Option<usize> {
let index_entry = self.index.get(log_idx)?;
if let Some(data_entry) = &index_entry.data {
Some(data_entry.length.to_usize())
} else {
Some(0)
}
}
fn get_term(&mut self, log_idx: LogIdx) -> Option<TermId> {
if log_idx == self.index.prev_log_idx() {
self.index.prev_log_term()
} else {
self.index.get(log_idx).map(|entry| entry.term)
}
}
fn prev_idx(&self) -> LogIdx {
self.index.prev_log_idx()
}
fn last_idx(&self) -> LogIdx {
self.index.last_log_idx()
}
fn last_term(&self) -> TermId {
self.index.last_log_term()
}
}

View File

@ -0,0 +1,128 @@
/*
* Copyright (C) 2019 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
use crate::prelude::*;
use num_traits::{ToPrimitive};
use sgx_ffi::util::{SecretValue};
use crate::protobufs::raft::LogEntry;
use crate::raft::*;
use crate::storage::storage_data::*;
use crate::util::*;
pub struct RaftLogData {
storage: StorageData,
head: usize,
tail: usize,
}
#[derive(Clone, Copy)]
pub struct RaftLogDataEntry {
pub nonce: StorageDataNonce,
pub offset: usize,
pub length: u32,
}
impl RaftLogData {
pub fn new(data_size: usize) -> Result<Self, ()> {
Ok(Self {
storage: StorageData::new(data_size)?,
head: Default::default(),
tail: Default::default(),
})
}
pub fn len(&self) -> usize {
if let Some(len) = self.head.checked_sub(self.tail) {
len
} else {
self.storage.len().saturating_sub(self.tail)
.saturating_add(self.head)
}
}
pub fn append(&mut self, log_entry: LogEntry) -> Result<RaftLogDataEntry, RaftLogAppendError> {
let append_len = log_entry.data.len().saturating_add(StorageData::tag_len().into());
let append_len = match append_len.to_u32() {
Some(append_len) if append_len.to_usize() < self.storage.len() => append_len,
Some(_) | None => {
error!("transaction too large at {} bytes (have {} bytes of storage)", log_entry.data.len(), self.storage.len());
return Err(RaftLogAppendError::TooLarge { size: append_len });
}
};
let mut offset = self.head;
let mut new_head = offset.saturating_add(append_len.to_usize());
if new_head >= self.storage.len() {
if self.tail > 0 {
offset = 0;
new_head = append_len.to_usize();
} else {
return Err(RaftLogAppendError::OutOfSpace { log_entry });
}
}
if offset < self.tail && new_head >= self.tail {
Err(RaftLogAppendError::OutOfSpace { log_entry })
} else {
self.head = new_head;
match self.storage.write(offset, log_entry.into_data()) {
Ok(nonce) => {
Ok(RaftLogDataEntry { nonce, offset, length: append_len })
}
Err(()) => {
error!("wrote out of bounds to raft log at {} len {}", offset, append_len);
Err(RaftLogAppendError::InternalError)
}
}
}
}
pub fn cancel(&mut self, cancelled_data_entries: Vec<RaftLogDataEntry>) -> Vec<SecretValue<Vec<u8>>> {
if let Some(cancel_from_data_entry) = cancelled_data_entries.get(0) {
self.head = cancel_from_data_entry.offset;
}
let mut cancelled_data = Vec::new();
for cancelled_data_entry in cancelled_data_entries {
if let Some(data) = self.read(&cancelled_data_entry) {
cancelled_data.push(data);
}
}
cancelled_data
}
pub fn pop_front_to(&mut self, popped_entry: &RaftLogDataEntry) {
let tail = popped_entry.offset.saturating_add(popped_entry.length.to_usize());
if tail < self.storage.len() {
self.tail = tail;
} else {
self.tail = Default::default();
}
}
pub fn read(&self, data_entry: &RaftLogDataEntry) -> Option<SecretValue<Vec<u8>>> {
if let Ok(data) = self.storage.read(data_entry.offset, data_entry.length.to_usize(), data_entry.nonce) {
Some(data)
} else {
error!("error reading raft log entry at offset {} length {}", data_entry.offset, data_entry.length);
None
}
}
}

Some files were not shown because too many files have changed in this diff Show More