Add dependencies/vendor (whatsapp)
This commit is contained in:
674
vendor/go.mau.fi/libsignal/LICENSE
vendored
Normal file
674
vendor/go.mau.fi/libsignal/LICENSE
vendored
Normal file
@@ -0,0 +1,674 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 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 General Public License is a free, copyleft license for
|
||||
software and other kinds of works.
|
||||
|
||||
The licenses for most software and other practical works are designed
|
||||
to take away your freedom to share and change the works. By contrast,
|
||||
the GNU General Public License is 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. We, the Free Software Foundation, use the
|
||||
GNU General Public License for most of our software; it applies also to
|
||||
any other work released this way by its authors. You can apply it to
|
||||
your programs, too.
|
||||
|
||||
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.
|
||||
|
||||
To protect your rights, we need to prevent others from denying you
|
||||
these rights or asking you to surrender the rights. Therefore, you have
|
||||
certain responsibilities if you distribute copies of the software, or if
|
||||
you modify it: responsibilities to respect the freedom of others.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must pass on to the recipients the same
|
||||
freedoms that you received. You must make sure that they, too, receive
|
||||
or can get the source code. And you must show them these terms so they
|
||||
know their rights.
|
||||
|
||||
Developers that use the GNU GPL protect your rights with two steps:
|
||||
(1) assert copyright on the software, and (2) offer you this License
|
||||
giving you legal permission to copy, distribute and/or modify it.
|
||||
|
||||
For the developers' and authors' protection, the GPL clearly explains
|
||||
that there is no warranty for this free software. For both users' and
|
||||
authors' sake, the GPL requires that modified versions be marked as
|
||||
changed, so that their problems will not be attributed erroneously to
|
||||
authors of previous versions.
|
||||
|
||||
Some devices are designed to deny users access to install or run
|
||||
modified versions of the software inside them, although the manufacturer
|
||||
can do so. This is fundamentally incompatible with the aim of
|
||||
protecting users' freedom to change the software. The systematic
|
||||
pattern of such abuse occurs in the area of products for individuals to
|
||||
use, which is precisely where it is most unacceptable. Therefore, we
|
||||
have designed this version of the GPL to prohibit the practice for those
|
||||
products. If such problems arise substantially in other domains, we
|
||||
stand ready to extend this provision to those domains in future versions
|
||||
of the GPL, as needed to protect the freedom of users.
|
||||
|
||||
Finally, every program is threatened constantly by software patents.
|
||||
States should not allow patents to restrict development and use of
|
||||
software on general-purpose computers, but in those that do, we wish to
|
||||
avoid the special danger that patents applied to a free program could
|
||||
make it effectively proprietary. To prevent this, the GPL assures that
|
||||
patents cannot be used to render the program non-free.
|
||||
|
||||
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 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. Use with the GNU Affero General Public License.
|
||||
|
||||
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 Affero 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 special requirements of the GNU Affero General Public License,
|
||||
section 13, concerning interaction through a network will apply to the
|
||||
combination as such.
|
||||
|
||||
14. Revised Versions of this License.
|
||||
|
||||
The Free Software Foundation may publish revised and/or new versions of
|
||||
the GNU 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 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 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 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.
|
||||
|
||||
libsignal-protocol-go
|
||||
Copyright (C) 2017 RadicalApp LLC
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU 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 the program does terminal interaction, make it output a short
|
||||
notice like this when it starts in an interactive mode:
|
||||
|
||||
libsignal-protocol-go Copyright (C) 2017 RadicalApp LLC
|
||||
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, your program's commands
|
||||
might be different; for a GUI interface, you would use an "about box".
|
||||
|
||||
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 GPL, see
|
||||
<http://www.gnu.org/licenses/>.
|
||||
|
||||
The GNU General Public License does not permit incorporating your program
|
||||
into proprietary programs. If your program is a subroutine library, you
|
||||
may consider it more useful to permit linking proprietary applications with
|
||||
the library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License. But first, please read
|
||||
<http://www.gnu.org/philosophy/why-not-lgpl.html>.
|
||||
101
vendor/go.mau.fi/libsignal/cipher/Cbc.go
vendored
Normal file
101
vendor/go.mau.fi/libsignal/cipher/Cbc.go
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
CBC describes a block cipher mode. In cryptography, a block cipher mode of operation is an algorithm that uses a
|
||||
block cipher to provide an information service such as confidentiality or authenticity. A block cipher by itself
|
||||
is only suitable for the secure cryptographic transformation (encryption or decryption) of one fixed-length group of
|
||||
bits called a block. A mode of operation describes how to repeatedly apply a cipher's single-block operation to
|
||||
securely transform amounts of data larger than a block.
|
||||
|
||||
This package simplifies the usage of AES-256-CBC.
|
||||
*/
|
||||
package cipher
|
||||
|
||||
/*
|
||||
Some code is provided by the GitHub user locked (github.com/locked):
|
||||
https://gist.github.com/locked/b066aa1ddeb2b28e855e
|
||||
Thanks!
|
||||
*/
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
/*
|
||||
Decrypt is a function that decrypts a given cipher text with a provided key and initialization vector(iv).
|
||||
*/
|
||||
func DecryptCbc(iv, key, ciphertext []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(ciphertext) < aes.BlockSize {
|
||||
return nil, fmt.Errorf("ciphertext is shorter then block size: %d / %d", len(ciphertext), aes.BlockSize)
|
||||
}
|
||||
|
||||
if iv == nil {
|
||||
iv = ciphertext[:aes.BlockSize]
|
||||
ciphertext = ciphertext[aes.BlockSize:]
|
||||
}
|
||||
|
||||
cbc := cipher.NewCBCDecrypter(block, iv)
|
||||
cbc.CryptBlocks(ciphertext, ciphertext)
|
||||
|
||||
return unpad(ciphertext)
|
||||
}
|
||||
|
||||
/*
|
||||
Encrypt is a function that encrypts plaintext with a given key and an optional initialization vector(iv).
|
||||
*/
|
||||
func EncryptCbc(iv, key, plaintext []byte) ([]byte, error) {
|
||||
plaintext = pad(plaintext, aes.BlockSize)
|
||||
|
||||
if len(plaintext)%aes.BlockSize != 0 {
|
||||
return nil, fmt.Errorf("plaintext is not a multiple of the block size: %d / %d", len(plaintext), aes.BlockSize)
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ciphertext []byte
|
||||
if iv == nil {
|
||||
ciphertext = make([]byte, aes.BlockSize+len(plaintext))
|
||||
iv := ciphertext[:aes.BlockSize]
|
||||
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cbc := cipher.NewCBCEncrypter(block, iv)
|
||||
cbc.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
|
||||
} else {
|
||||
ciphertext = make([]byte, len(plaintext))
|
||||
|
||||
cbc := cipher.NewCBCEncrypter(block, iv)
|
||||
cbc.CryptBlocks(ciphertext, plaintext)
|
||||
}
|
||||
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
func pad(ciphertext []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(ciphertext)%blockSize
|
||||
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
return append(ciphertext, padtext...)
|
||||
}
|
||||
|
||||
func unpad(src []byte) ([]byte, error) {
|
||||
length := len(src)
|
||||
padLen := int(src[length-1])
|
||||
|
||||
if padLen > length {
|
||||
return nil, fmt.Errorf("padding is greater then the length: %d / %d", padLen, length)
|
||||
}
|
||||
|
||||
return src[:(length - padLen)], nil
|
||||
}
|
||||
105
vendor/go.mau.fi/libsignal/cipher/Cipher.go
vendored
Normal file
105
vendor/go.mau.fi/libsignal/cipher/Cipher.go
vendored
Normal file
@@ -0,0 +1,105 @@
|
||||
// Package cipher is a package for common encrypt/decrypt of symmetric key messages.
|
||||
package cipher
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"errors"
|
||||
)
|
||||
|
||||
// Decrypt will use the given key, iv, and ciphertext and return
|
||||
// the plaintext bytes.
|
||||
func Decrypt(iv, key, ciphertext []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(ciphertext) < aes.BlockSize {
|
||||
return nil, errors.New("ciphertext too short")
|
||||
}
|
||||
cbc := cipher.NewCBCDecrypter(block, iv)
|
||||
cbc.CryptBlocks(ciphertext, ciphertext)
|
||||
|
||||
unpaddedText, err := pkcs7Unpad(ciphertext, aes.BlockSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return unpaddedText, nil
|
||||
}
|
||||
|
||||
// Encrypt will use the given iv, key, and plaintext bytes
|
||||
// and return ciphertext bytes.
|
||||
func Encrypt(iv, key, plaintext []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
paddedText, err := pkcs7Pad(plaintext, block.BlockSize())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ciphertext := make([]byte, len(paddedText))
|
||||
mode := cipher.NewCBCEncrypter(block, iv)
|
||||
mode.CryptBlocks(ciphertext, paddedText)
|
||||
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
// PKCS7 padding.
|
||||
|
||||
// PKCS7 errors.
|
||||
var (
|
||||
// ErrInvalidBlockSize indicates hash blocksize <= 0.
|
||||
ErrInvalidBlockSize = errors.New("invalid blocksize")
|
||||
|
||||
// ErrInvalidPKCS7Data indicates bad input to PKCS7 pad or unpad.
|
||||
ErrInvalidPKCS7Data = errors.New("invalid PKCS7 data (empty or not padded)")
|
||||
|
||||
// ErrInvalidPKCS7Padding indicates PKCS7 unpad fails to bad input.
|
||||
ErrInvalidPKCS7Padding = errors.New("invalid padding on input")
|
||||
)
|
||||
|
||||
// pkcs7Pad right-pads the given byte slice with 1 to n bytes, where
|
||||
// n is the block size. The size of the result is x times n, where x
|
||||
// is at least 1.
|
||||
func pkcs7Pad(b []byte, blocksize int) ([]byte, error) {
|
||||
if blocksize <= 0 {
|
||||
return nil, ErrInvalidBlockSize
|
||||
}
|
||||
if b == nil || len(b) == 0 {
|
||||
return nil, ErrInvalidPKCS7Data
|
||||
}
|
||||
n := blocksize - (len(b) % blocksize)
|
||||
pb := make([]byte, len(b)+n)
|
||||
copy(pb, b)
|
||||
copy(pb[len(b):], bytes.Repeat([]byte{byte(n)}, n))
|
||||
return pb, nil
|
||||
}
|
||||
|
||||
// pkcs7Unpad validates and unpads data from the given bytes slice.
|
||||
// The returned value will be 1 to n bytes smaller depending on the
|
||||
// amount of padding, where n is the block size.
|
||||
func pkcs7Unpad(b []byte, blocksize int) ([]byte, error) {
|
||||
if blocksize <= 0 {
|
||||
return nil, ErrInvalidBlockSize
|
||||
}
|
||||
if b == nil || len(b) == 0 {
|
||||
return nil, ErrInvalidPKCS7Data
|
||||
}
|
||||
if len(b)%blocksize != 0 {
|
||||
return nil, ErrInvalidPKCS7Padding
|
||||
}
|
||||
c := b[len(b)-1]
|
||||
n := int(c)
|
||||
if n == 0 || n > len(b) {
|
||||
return nil, ErrInvalidPKCS7Padding
|
||||
}
|
||||
for i := 0; i < n; i++ {
|
||||
if b[len(b)-n+i] != c {
|
||||
return nil, ErrInvalidPKCS7Padding
|
||||
}
|
||||
}
|
||||
return b[:len(b)-n], nil
|
||||
}
|
||||
109
vendor/go.mau.fi/libsignal/ecc/Curve.go
vendored
Normal file
109
vendor/go.mau.fi/libsignal/ecc/Curve.go
vendored
Normal file
@@ -0,0 +1,109 @@
|
||||
package ecc
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/curve25519"
|
||||
|
||||
"go.mau.fi/libsignal/logger"
|
||||
)
|
||||
|
||||
// DjbType is the Diffie-Hellman curve type (curve25519) created by D. J. Bernstein.
|
||||
const DjbType = 0x05
|
||||
|
||||
var ErrBadKeyType = errors.New("bad key type")
|
||||
|
||||
// DecodePoint will take the given bytes and offset and return an ECPublicKeyable object.
|
||||
// This is used to check the byte at the given offset in the byte array for a special
|
||||
// "type" byte that will determine the key type. Currently only DJB EC keys are supported.
|
||||
func DecodePoint(bytes []byte, offset int) (ECPublicKeyable, error) {
|
||||
keyType := bytes[offset] & 0xFF
|
||||
|
||||
switch keyType {
|
||||
case DjbType:
|
||||
keyBytes := [32]byte{}
|
||||
copy(keyBytes[:], bytes[offset+1:])
|
||||
return NewDjbECPublicKey(keyBytes), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("%w %d", ErrBadKeyType, keyType)
|
||||
}
|
||||
}
|
||||
|
||||
func CreateKeyPair(privateKey []byte) *ECKeyPair {
|
||||
var private, public [32]byte
|
||||
copy(private[:], privateKey)
|
||||
|
||||
private[0] &= 248
|
||||
private[31] &= 127
|
||||
private[31] |= 64
|
||||
|
||||
curve25519.ScalarBaseMult(&public, &private)
|
||||
|
||||
// Put data into our keypair struct
|
||||
djbECPub := NewDjbECPublicKey(public)
|
||||
djbECPriv := NewDjbECPrivateKey(private)
|
||||
keypair := NewECKeyPair(djbECPub, djbECPriv)
|
||||
|
||||
logger.Debug("Returning keypair: ", keypair)
|
||||
return keypair
|
||||
}
|
||||
|
||||
// GenerateKeyPair returns an EC Key Pair.
|
||||
func GenerateKeyPair() (*ECKeyPair, error) {
|
||||
// logger.Debug("Generating EC Key Pair...")
|
||||
// Get cryptographically secure random numbers.
|
||||
random := rand.Reader
|
||||
|
||||
// Create a byte array for our public and private keys.
|
||||
var private, public [32]byte
|
||||
|
||||
// Generate some random data
|
||||
_, err := io.ReadFull(random, private[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Documented at: http://cr.yp.to/ecdh.html
|
||||
private[0] &= 248
|
||||
private[31] &= 127
|
||||
private[31] |= 64
|
||||
|
||||
curve25519.ScalarBaseMult(&public, &private)
|
||||
|
||||
// Put data into our keypair struct
|
||||
djbECPub := NewDjbECPublicKey(public)
|
||||
djbECPriv := NewDjbECPrivateKey(private)
|
||||
keypair := NewECKeyPair(djbECPub, djbECPriv)
|
||||
|
||||
// logger.Debug("Returning keypair: ", keypair)
|
||||
|
||||
return keypair, nil
|
||||
}
|
||||
|
||||
// VerifySignature verifies that the message was signed with the given key.
|
||||
func VerifySignature(signingKey ECPublicKeyable, message []byte, signature [64]byte) bool {
|
||||
logger.Debug("Verifying signature of bytes: ", message)
|
||||
publicKey := signingKey.PublicKey()
|
||||
valid := verify(publicKey, message, &signature)
|
||||
logger.Debug("Signature valid: ", valid)
|
||||
return valid
|
||||
}
|
||||
|
||||
// CalculateSignature signs a message with the given private key.
|
||||
func CalculateSignature(signingKey ECPrivateKeyable, message []byte) [64]byte {
|
||||
logger.Debug("Signing bytes with signing key")
|
||||
// Get cryptographically secure random numbers.
|
||||
var random [64]byte
|
||||
r := rand.Reader
|
||||
io.ReadFull(r, random[:])
|
||||
|
||||
// Get the private key.
|
||||
privateKey := signingKey.Serialize()
|
||||
|
||||
// Sign the message.
|
||||
signature := sign(&privateKey, message, random)
|
||||
return *signature
|
||||
}
|
||||
29
vendor/go.mau.fi/libsignal/ecc/DjbECPublicKey.go
vendored
Normal file
29
vendor/go.mau.fi/libsignal/ecc/DjbECPublicKey.go
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
package ecc
|
||||
|
||||
// NewDjbECPublicKey creates a new Curve25519 public key with the given bytes.
|
||||
func NewDjbECPublicKey(publicKey [32]byte) *DjbECPublicKey {
|
||||
key := DjbECPublicKey{
|
||||
publicKey: publicKey,
|
||||
}
|
||||
return &key
|
||||
}
|
||||
|
||||
// DjbECPublicKey implements the ECPublicKey interface and uses Curve25519.
|
||||
type DjbECPublicKey struct {
|
||||
publicKey [32]byte
|
||||
}
|
||||
|
||||
// PublicKey returns the EC public key as a byte array.
|
||||
func (d *DjbECPublicKey) PublicKey() [32]byte {
|
||||
return d.publicKey
|
||||
}
|
||||
|
||||
// Serialize returns the public key prepended by the DjbType value.
|
||||
func (d *DjbECPublicKey) Serialize() []byte {
|
||||
return append([]byte{DjbType}, d.publicKey[:]...)
|
||||
}
|
||||
|
||||
// Type returns the DjbType value.
|
||||
func (d *DjbECPublicKey) Type() int {
|
||||
return DjbType
|
||||
}
|
||||
29
vendor/go.mau.fi/libsignal/ecc/DkbECPrivateKey.go
vendored
Normal file
29
vendor/go.mau.fi/libsignal/ecc/DkbECPrivateKey.go
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
package ecc
|
||||
|
||||
// NewDjbECPrivateKey returns a new EC private key with the given bytes.
|
||||
func NewDjbECPrivateKey(key [32]byte) *DjbECPrivateKey {
|
||||
private := DjbECPrivateKey{
|
||||
privateKey: key,
|
||||
}
|
||||
return &private
|
||||
}
|
||||
|
||||
// DjbECPrivateKey implements the ECPrivateKey interface and uses Curve25519.
|
||||
type DjbECPrivateKey struct {
|
||||
privateKey [32]byte
|
||||
}
|
||||
|
||||
// PrivateKey returns the private key as a byte-array.
|
||||
func (d *DjbECPrivateKey) PrivateKey() [32]byte {
|
||||
return d.privateKey
|
||||
}
|
||||
|
||||
// Serialize returns the private key as a byte-array.
|
||||
func (d *DjbECPrivateKey) Serialize() [32]byte {
|
||||
return d.privateKey
|
||||
}
|
||||
|
||||
// Type returns the EC type value.
|
||||
func (d *DjbECPrivateKey) Type() int {
|
||||
return DjbType
|
||||
}
|
||||
3
vendor/go.mau.fi/libsignal/ecc/Doc.go
vendored
Normal file
3
vendor/go.mau.fi/libsignal/ecc/Doc.go
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package ecc provides a way to generate, sign, and use Elliptic-Curve
|
||||
// X25519 Cryptography keys.
|
||||
package ecc
|
||||
27
vendor/go.mau.fi/libsignal/ecc/ECKeyPair.go
vendored
Normal file
27
vendor/go.mau.fi/libsignal/ecc/ECKeyPair.go
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
package ecc
|
||||
|
||||
// NewECKeyPair returns a new elliptic curve keypair given the specified public and private keys.
|
||||
func NewECKeyPair(publicKey ECPublicKeyable, privateKey ECPrivateKeyable) *ECKeyPair {
|
||||
keypair := ECKeyPair{
|
||||
publicKey: publicKey,
|
||||
privateKey: privateKey,
|
||||
}
|
||||
|
||||
return &keypair
|
||||
}
|
||||
|
||||
// ECKeyPair is a combination of both public and private elliptic curve keys.
|
||||
type ECKeyPair struct {
|
||||
publicKey ECPublicKeyable
|
||||
privateKey ECPrivateKeyable
|
||||
}
|
||||
|
||||
// PublicKey returns the public key from the key pair.
|
||||
func (e *ECKeyPair) PublicKey() ECPublicKeyable {
|
||||
return e.publicKey
|
||||
}
|
||||
|
||||
// PrivateKey returns the private key from the key pair.
|
||||
func (e *ECKeyPair) PrivateKey() ECPrivateKeyable {
|
||||
return e.privateKey
|
||||
}
|
||||
7
vendor/go.mau.fi/libsignal/ecc/ECPrivateKey.go
vendored
Normal file
7
vendor/go.mau.fi/libsignal/ecc/ECPrivateKey.go
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
package ecc
|
||||
|
||||
// ECPrivateKeyable is an interface for all elliptic curve private keys.
|
||||
type ECPrivateKeyable interface {
|
||||
Serialize() [32]byte
|
||||
Type() int
|
||||
}
|
||||
11
vendor/go.mau.fi/libsignal/ecc/ECPublicKey.go
vendored
Normal file
11
vendor/go.mau.fi/libsignal/ecc/ECPublicKey.go
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
package ecc
|
||||
|
||||
// KeySize is the size of EC keys (32) with the EC type byte prepended to it.
|
||||
const KeySize int = 33
|
||||
|
||||
// ECPublicKeyable is an interface for all elliptic curve public keys.
|
||||
type ECPublicKeyable interface {
|
||||
Serialize() []byte
|
||||
Type() int
|
||||
PublicKey() [32]byte
|
||||
}
|
||||
97
vendor/go.mau.fi/libsignal/ecc/SignCurve25519.go
vendored
Normal file
97
vendor/go.mau.fi/libsignal/ecc/SignCurve25519.go
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
package ecc
|
||||
|
||||
// Package curve25519sign implements a signature scheme based on Curve25519 keys.
|
||||
// See https://moderncrypto.org/mail-archive/curves/2014/000205.html for details.
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/sha512"
|
||||
|
||||
"filippo.io/edwards25519"
|
||||
"filippo.io/edwards25519/field"
|
||||
)
|
||||
|
||||
// sign signs the message with privateKey and returns a signature as a byte slice.
|
||||
func sign(privateKey *[32]byte, message []byte, random [64]byte) *[64]byte {
|
||||
|
||||
// Calculate Ed25519 public key from Curve25519 private key
|
||||
var A edwards25519.Point
|
||||
privateKeyScalar, _ := edwards25519.NewScalar().SetBytesWithClamping(privateKey[:])
|
||||
A.ScalarBaseMult(privateKeyScalar)
|
||||
publicKey := *(*[32]byte)(A.Bytes())
|
||||
|
||||
// Calculate r
|
||||
diversifier := [32]byte{
|
||||
0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
|
||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}
|
||||
|
||||
var r [64]byte
|
||||
hash := sha512.New()
|
||||
hash.Write(diversifier[:])
|
||||
hash.Write(privateKey[:])
|
||||
hash.Write(message)
|
||||
hash.Write(random[:])
|
||||
hash.Sum(r[:0])
|
||||
|
||||
// Calculate R
|
||||
var rReduced *edwards25519.Scalar
|
||||
rReduced, _ = edwards25519.NewScalar().SetUniformBytes(r[:])
|
||||
var R edwards25519.Point
|
||||
R.ScalarBaseMult(rReduced)
|
||||
|
||||
var encodedR [32]byte
|
||||
encodedR = *(*[32]byte)(R.Bytes())
|
||||
|
||||
// Calculate S = r + SHA2-512(R || A_ed || msg) * a (mod L)
|
||||
var hramDigest [64]byte
|
||||
hash.Reset()
|
||||
hash.Write(encodedR[:])
|
||||
hash.Write(publicKey[:])
|
||||
hash.Write(message)
|
||||
hash.Sum(hramDigest[:0])
|
||||
hramDigestReduced, _ := edwards25519.NewScalar().SetUniformBytes(hramDigest[:])
|
||||
|
||||
sScalar := edwards25519.NewScalar().MultiplyAdd(hramDigestReduced, privateKeyScalar, rReduced)
|
||||
s := *(*[32]byte)(sScalar.Bytes())
|
||||
|
||||
signature := new([64]byte)
|
||||
copy(signature[:], encodedR[:])
|
||||
copy(signature[32:], s[:])
|
||||
signature[63] |= publicKey[31] & 0x80
|
||||
|
||||
return signature
|
||||
}
|
||||
|
||||
// verify checks whether the message has a valid signature.
|
||||
func verify(publicKey [32]byte, message []byte, signature *[64]byte) bool {
|
||||
|
||||
publicKey[31] &= 0x7F
|
||||
|
||||
/* Convert the Curve25519 public key into an Ed25519 public key. In
|
||||
particular, convert Curve25519's "montgomery" x-coordinate into an
|
||||
Ed25519 "edwards" y-coordinate:
|
||||
|
||||
ed_y = (mont_x - 1) / (mont_x + 1)
|
||||
|
||||
NOTE: mont_x=-1 is converted to ed_y=0 since fe_invert is mod-exp
|
||||
|
||||
Then move the sign bit into the pubkey from the signature.
|
||||
*/
|
||||
|
||||
var edY, one, montX, montXMinusOne, montXPlusOne field.Element
|
||||
_, _ = montX.SetBytes(publicKey[:])
|
||||
_ = one.One()
|
||||
montXMinusOne.Subtract(&montX, &one)
|
||||
montXPlusOne.Add(&montX, &one)
|
||||
montXPlusOne.Invert(&montXPlusOne)
|
||||
edY.Multiply(&montXMinusOne, &montXPlusOne)
|
||||
|
||||
A_ed := *(*[32]byte)(edY.Bytes())
|
||||
|
||||
A_ed[31] |= signature[63] & 0x80
|
||||
signature[63] &= 0x7F
|
||||
|
||||
return ed25519.Verify(A_ed[:], message, signature[:])
|
||||
}
|
||||
141
vendor/go.mau.fi/libsignal/groups/GroupCipher.go
vendored
Normal file
141
vendor/go.mau.fi/libsignal/groups/GroupCipher.go
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
package groups
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.mau.fi/libsignal/cipher"
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/groups/ratchet"
|
||||
"go.mau.fi/libsignal/groups/state/record"
|
||||
"go.mau.fi/libsignal/groups/state/store"
|
||||
"go.mau.fi/libsignal/protocol"
|
||||
"go.mau.fi/libsignal/signalerror"
|
||||
)
|
||||
|
||||
// NewGroupCipher will return a new group message cipher that can be used for
|
||||
// encrypt/decrypt operations.
|
||||
func NewGroupCipher(builder *SessionBuilder, senderKeyID *protocol.SenderKeyName,
|
||||
senderKeyStore store.SenderKey) *GroupCipher {
|
||||
|
||||
return &GroupCipher{
|
||||
senderKeyID: senderKeyID,
|
||||
senderKeyStore: senderKeyStore,
|
||||
sessionBuilder: builder,
|
||||
}
|
||||
}
|
||||
|
||||
// GroupCipher is the main entry point for group encrypt/decrypt operations.
|
||||
// Once a session has been established, this can be used for
|
||||
// all encrypt/decrypt operations within that session.
|
||||
type GroupCipher struct {
|
||||
senderKeyID *protocol.SenderKeyName
|
||||
senderKeyStore store.SenderKey
|
||||
sessionBuilder *SessionBuilder
|
||||
}
|
||||
|
||||
// Encrypt will take the given message in bytes and return encrypted bytes.
|
||||
func (c *GroupCipher) Encrypt(plaintext []byte) (protocol.GroupCiphertextMessage, error) {
|
||||
// Load the sender key based on id from our store.
|
||||
keyRecord := c.senderKeyStore.LoadSenderKey(c.senderKeyID)
|
||||
senderKeyState, err := keyRecord.SenderKeyState()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get the message key from the senderkey state.
|
||||
senderKey, err := senderKeyState.SenderChainKey().SenderMessageKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Encrypt the plaintext.
|
||||
ciphertext, err := cipher.EncryptCbc(senderKey.Iv(), senderKey.CipherKey(), plaintext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
senderKeyMessage := protocol.NewSenderKeyMessage(
|
||||
senderKeyState.KeyID(),
|
||||
senderKey.Iteration(),
|
||||
ciphertext,
|
||||
senderKeyState.SigningKey().PrivateKey(),
|
||||
c.sessionBuilder.serializer.SenderKeyMessage,
|
||||
)
|
||||
|
||||
senderKeyState.SetSenderChainKey(senderKeyState.SenderChainKey().Next())
|
||||
c.senderKeyStore.StoreSenderKey(c.senderKeyID, keyRecord)
|
||||
|
||||
return senderKeyMessage, nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts the given message using an existing session that
|
||||
// is stored in the senderKey store.
|
||||
func (c *GroupCipher) Decrypt(senderKeyMessage *protocol.SenderKeyMessage) ([]byte, error) {
|
||||
keyRecord := c.senderKeyStore.LoadSenderKey(c.senderKeyID)
|
||||
|
||||
if keyRecord.IsEmpty() {
|
||||
return nil, fmt.Errorf("%w for %s in %s", signalerror.ErrNoSenderKeyForUser, c.senderKeyID.Sender().String(), c.senderKeyID.GroupID())
|
||||
}
|
||||
|
||||
// Get the senderkey state by id.
|
||||
senderKeyState, err := keyRecord.GetSenderKeyStateByID(senderKeyMessage.KeyID())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Verify the signature of the senderkey message.
|
||||
verified := c.verifySignature(senderKeyState.SigningKey().PublicKey(), senderKeyMessage)
|
||||
if !verified {
|
||||
return nil, signalerror.ErrSenderKeyStateVerificationFailed
|
||||
}
|
||||
|
||||
senderKey, err := c.getSenderKey(senderKeyState, senderKeyMessage.Iteration())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Decrypt the message ciphertext.
|
||||
plaintext, err := cipher.DecryptCbc(senderKey.Iv(), senderKey.CipherKey(), senderKeyMessage.Ciphertext())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Store the sender key by id.
|
||||
c.senderKeyStore.StoreSenderKey(c.senderKeyID, keyRecord)
|
||||
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// verifySignature will verify the signature of the senderkey message with
|
||||
// the given public key.
|
||||
func (c *GroupCipher) verifySignature(signingPubKey ecc.ECPublicKeyable,
|
||||
senderKeyMessage *protocol.SenderKeyMessage) bool {
|
||||
|
||||
return ecc.VerifySignature(signingPubKey, senderKeyMessage.Serialize(), senderKeyMessage.Signature())
|
||||
}
|
||||
|
||||
func (c *GroupCipher) getSenderKey(senderKeyState *record.SenderKeyState, iteration uint32) (*ratchet.SenderMessageKey, error) {
|
||||
senderChainKey := senderKeyState.SenderChainKey()
|
||||
if senderChainKey.Iteration() > iteration {
|
||||
if senderKeyState.HasSenderMessageKey(iteration) {
|
||||
return senderKeyState.RemoveSenderMessageKey(iteration), nil
|
||||
}
|
||||
return nil, fmt.Errorf("%w (current: %d, received: %d)", signalerror.ErrOldCounter, senderChainKey.Iteration(), iteration)
|
||||
}
|
||||
|
||||
if iteration-senderChainKey.Iteration() > 2000 {
|
||||
return nil, signalerror.ErrTooFarIntoFuture
|
||||
}
|
||||
|
||||
for senderChainKey.Iteration() < iteration {
|
||||
senderMessageKey, err := senderChainKey.SenderMessageKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
senderKeyState.AddSenderMessageKey(senderMessageKey)
|
||||
senderChainKey = senderChainKey.Next()
|
||||
}
|
||||
|
||||
senderKeyState.SetSenderChainKey(senderChainKey.Next())
|
||||
return senderChainKey.SenderMessageKey()
|
||||
}
|
||||
84
vendor/go.mau.fi/libsignal/groups/GroupSessionBuilder.go
vendored
Normal file
84
vendor/go.mau.fi/libsignal/groups/GroupSessionBuilder.go
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
// Package groups is responsible for setting up group SenderKey encrypted sessions.
|
||||
// Once a session has been established, GroupCipher can be used to encrypt/decrypt
|
||||
// messages in that session.
|
||||
//
|
||||
// The built sessions are unidirectional: they can be used either for sending or
|
||||
// for receiving, but not both. Sessions are constructed per (groupId + senderId +
|
||||
// deviceId) tuple. Remote logical users are identified by their senderId, and each
|
||||
// logical recipientId can have multiple physical devices.
|
||||
package groups
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/groups/state/record"
|
||||
"go.mau.fi/libsignal/groups/state/store"
|
||||
"go.mau.fi/libsignal/protocol"
|
||||
"go.mau.fi/libsignal/serialize"
|
||||
"go.mau.fi/libsignal/util/keyhelper"
|
||||
)
|
||||
|
||||
// NewGroupSessionBuilder will return a new group session builder.
|
||||
func NewGroupSessionBuilder(senderKeyStore store.SenderKey,
|
||||
serializer *serialize.Serializer) *SessionBuilder {
|
||||
|
||||
return &SessionBuilder{
|
||||
senderKeyStore: senderKeyStore,
|
||||
serializer: serializer,
|
||||
}
|
||||
}
|
||||
|
||||
// SessionBuilder is a structure for building group sessions.
|
||||
type SessionBuilder struct {
|
||||
senderKeyStore store.SenderKey
|
||||
serializer *serialize.Serializer
|
||||
}
|
||||
|
||||
// Process will process an incoming group message and set up the corresponding
|
||||
// session for it.
|
||||
func (b *SessionBuilder) Process(senderKeyName *protocol.SenderKeyName,
|
||||
msg *protocol.SenderKeyDistributionMessage) {
|
||||
|
||||
senderKeyRecord := b.senderKeyStore.LoadSenderKey(senderKeyName)
|
||||
if senderKeyRecord == nil {
|
||||
senderKeyRecord = record.NewSenderKey(b.serializer.SenderKeyRecord, b.serializer.SenderKeyState)
|
||||
}
|
||||
senderKeyRecord.AddSenderKeyState(msg.ID(), msg.Iteration(), msg.ChainKey(), msg.SignatureKey())
|
||||
b.senderKeyStore.StoreSenderKey(senderKeyName, senderKeyRecord)
|
||||
}
|
||||
|
||||
// Create will create a new group session for the given name.
|
||||
func (b *SessionBuilder) Create(senderKeyName *protocol.SenderKeyName) (*protocol.SenderKeyDistributionMessage, error) {
|
||||
// Load the senderkey by name
|
||||
senderKeyRecord := b.senderKeyStore.LoadSenderKey(senderKeyName)
|
||||
|
||||
// If the record is empty, generate new keys.
|
||||
if senderKeyRecord == nil || senderKeyRecord.IsEmpty() {
|
||||
senderKeyRecord = record.NewSenderKey(b.serializer.SenderKeyRecord, b.serializer.SenderKeyState)
|
||||
signingKey, err := keyhelper.GenerateSenderSigningKey()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
senderKeyRecord.SetSenderKeyState(
|
||||
keyhelper.GenerateSenderKeyID(), 0,
|
||||
keyhelper.GenerateSenderKey(),
|
||||
signingKey,
|
||||
)
|
||||
b.senderKeyStore.StoreSenderKey(senderKeyName, senderKeyRecord)
|
||||
}
|
||||
|
||||
// Get the senderkey state.
|
||||
state, err := senderKeyRecord.SenderKeyState()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the group message to return.
|
||||
senderKeyDistributionMessage := protocol.NewSenderKeyDistributionMessage(
|
||||
state.KeyID(),
|
||||
state.SenderChainKey().Iteration(),
|
||||
state.SenderChainKey().Seed(),
|
||||
state.SigningKey().PublicKey(),
|
||||
b.serializer.SenderKeyDistributionMessage,
|
||||
)
|
||||
|
||||
return senderKeyDistributionMessage, nil
|
||||
}
|
||||
3
vendor/go.mau.fi/libsignal/groups/ratchet/Doc.go
vendored
Normal file
3
vendor/go.mau.fi/libsignal/groups/ratchet/Doc.go
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package ratchet provides the methods necessary to establish a ratchet
|
||||
// session for group messaging.
|
||||
package ratchet
|
||||
68
vendor/go.mau.fi/libsignal/groups/ratchet/SenderChainKey.go
vendored
Normal file
68
vendor/go.mau.fi/libsignal/groups/ratchet/SenderChainKey.go
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
package ratchet
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
)
|
||||
|
||||
var messageKeySeed = []byte{0x01}
|
||||
var chainKeySeed = []byte{0x02}
|
||||
|
||||
// NewSenderChainKey will return a new SenderChainKey.
|
||||
func NewSenderChainKey(iteration uint32, chainKey []byte) *SenderChainKey {
|
||||
return &SenderChainKey{
|
||||
iteration: iteration,
|
||||
chainKey: chainKey,
|
||||
}
|
||||
}
|
||||
|
||||
// NewSenderChainKeyFromStruct will return a new chain key object from the
|
||||
// given serializeable structure.
|
||||
func NewSenderChainKeyFromStruct(structure *SenderChainKeyStructure) *SenderChainKey {
|
||||
return &SenderChainKey{
|
||||
iteration: structure.Iteration,
|
||||
chainKey: structure.ChainKey,
|
||||
}
|
||||
}
|
||||
|
||||
// NewStructFromSenderChainKeys returns a serializeable structure of chain keys.
|
||||
func NewStructFromSenderChainKey(key *SenderChainKey) *SenderChainKeyStructure {
|
||||
return &SenderChainKeyStructure{
|
||||
Iteration: key.iteration,
|
||||
ChainKey: key.chainKey,
|
||||
}
|
||||
}
|
||||
|
||||
// SenderChainKeyStructure is a serializeable structure of SenderChainKeys.
|
||||
type SenderChainKeyStructure struct {
|
||||
Iteration uint32
|
||||
ChainKey []byte
|
||||
}
|
||||
|
||||
type SenderChainKey struct {
|
||||
iteration uint32
|
||||
chainKey []byte
|
||||
}
|
||||
|
||||
func (k *SenderChainKey) Iteration() uint32 {
|
||||
return k.iteration
|
||||
}
|
||||
|
||||
func (k *SenderChainKey) SenderMessageKey() (*SenderMessageKey, error) {
|
||||
return NewSenderMessageKey(k.iteration, k.getDerivative(messageKeySeed, k.chainKey))
|
||||
}
|
||||
|
||||
func (k *SenderChainKey) Next() *SenderChainKey {
|
||||
return NewSenderChainKey(k.iteration+1, k.getDerivative(chainKeySeed, k.chainKey))
|
||||
}
|
||||
|
||||
func (k *SenderChainKey) Seed() []byte {
|
||||
return k.chainKey
|
||||
}
|
||||
|
||||
func (k *SenderChainKey) getDerivative(seed []byte, key []byte) []byte {
|
||||
mac := hmac.New(sha256.New, key[:])
|
||||
mac.Write(seed)
|
||||
|
||||
return mac.Sum(nil)
|
||||
}
|
||||
89
vendor/go.mau.fi/libsignal/groups/ratchet/SenderMessageKey.go
vendored
Normal file
89
vendor/go.mau.fi/libsignal/groups/ratchet/SenderMessageKey.go
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
package ratchet
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/kdf"
|
||||
"go.mau.fi/libsignal/util/bytehelper"
|
||||
)
|
||||
|
||||
// KdfInfo is optional bytes to include in deriving secrets with KDF.
|
||||
const KdfInfo string = "WhisperGroup"
|
||||
|
||||
// NewSenderMessageKey will return a new sender message key using the given
|
||||
// iteration and seed.
|
||||
func NewSenderMessageKey(iteration uint32, seed []byte) (*SenderMessageKey, error) {
|
||||
derivative, err := kdf.DeriveSecrets(seed, nil, []byte(KdfInfo), 48)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Split our derived secrets into 2 parts
|
||||
parts := bytehelper.Split(derivative, 16, 32)
|
||||
|
||||
// Build the message key.
|
||||
senderKeyMessage := &SenderMessageKey{
|
||||
iteration: iteration,
|
||||
seed: seed,
|
||||
iv: parts[0],
|
||||
cipherKey: parts[1],
|
||||
}
|
||||
|
||||
return senderKeyMessage, nil
|
||||
}
|
||||
|
||||
// NewSenderMessageKeyFromStruct will return a new message key object from the
|
||||
// given serializeable structure.
|
||||
func NewSenderMessageKeyFromStruct(structure *SenderMessageKeyStructure) *SenderMessageKey {
|
||||
return &SenderMessageKey{
|
||||
iteration: structure.Iteration,
|
||||
iv: structure.IV,
|
||||
cipherKey: structure.CipherKey,
|
||||
seed: structure.Seed,
|
||||
}
|
||||
}
|
||||
|
||||
// NewStructFromSenderMessageKey returns a serializeable structure of message keys.
|
||||
func NewStructFromSenderMessageKey(key *SenderMessageKey) *SenderMessageKeyStructure {
|
||||
return &SenderMessageKeyStructure{
|
||||
CipherKey: key.cipherKey,
|
||||
Iteration: key.iteration,
|
||||
IV: key.iv,
|
||||
Seed: key.seed,
|
||||
}
|
||||
}
|
||||
|
||||
// SenderMessageKeyStructure is a serializeable structure of SenderMessageKeys.
|
||||
type SenderMessageKeyStructure struct {
|
||||
Iteration uint32
|
||||
IV []byte
|
||||
CipherKey []byte
|
||||
Seed []byte
|
||||
}
|
||||
|
||||
// SenderMessageKey is a structure for sender message keys used in group
|
||||
// messaging.
|
||||
type SenderMessageKey struct {
|
||||
iteration uint32
|
||||
iv []byte
|
||||
cipherKey []byte
|
||||
seed []byte
|
||||
}
|
||||
|
||||
// Iteration will return the sender message key's iteration.
|
||||
func (k *SenderMessageKey) Iteration() uint32 {
|
||||
return k.iteration
|
||||
}
|
||||
|
||||
// Iv will return the sender message key's initialization vector.
|
||||
func (k *SenderMessageKey) Iv() []byte {
|
||||
return k.iv
|
||||
}
|
||||
|
||||
// CipherKey will return the key in bytes.
|
||||
func (k *SenderMessageKey) CipherKey() []byte {
|
||||
return k.cipherKey
|
||||
}
|
||||
|
||||
// Seed will return the sender message key's seed.
|
||||
func (k *SenderMessageKey) Seed() []byte {
|
||||
return k.seed
|
||||
}
|
||||
2
vendor/go.mau.fi/libsignal/groups/state/record/Doc.go
vendored
Normal file
2
vendor/go.mau.fi/libsignal/groups/state/record/Doc.go
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package record provides the state and record of a group session.
|
||||
package record
|
||||
149
vendor/go.mau.fi/libsignal/groups/state/record/SenderKeyRecord.go
vendored
Normal file
149
vendor/go.mau.fi/libsignal/groups/state/record/SenderKeyRecord.go
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
package record
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/signalerror"
|
||||
)
|
||||
|
||||
const maxStates = 5
|
||||
|
||||
// SenderKeySerializer is an interface for serializing and deserializing
|
||||
// SenderKey objects into bytes. An implementation of this interface should be
|
||||
// used to encode/decode the object into JSON, Protobuffers, etc.
|
||||
type SenderKeySerializer interface {
|
||||
Serialize(preKey *SenderKeyStructure) []byte
|
||||
Deserialize(serialized []byte) (*SenderKeyStructure, error)
|
||||
}
|
||||
|
||||
// NewSenderKeyFromBytes will return a prekey record from the given bytes using the given serializer.
|
||||
func NewSenderKeyFromBytes(serialized []byte, serializer SenderKeySerializer,
|
||||
stateSerializer SenderKeyStateSerializer) (*SenderKey, error) {
|
||||
|
||||
// Use the given serializer to decode the senderkey record
|
||||
senderKeyStructure, err := serializer.Deserialize(serialized)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewSenderKeyFromStruct(senderKeyStructure, serializer, stateSerializer)
|
||||
}
|
||||
|
||||
// NewSenderKeyFromStruct returns a SenderKey record using the given serializable structure.
|
||||
func NewSenderKeyFromStruct(structure *SenderKeyStructure, serializer SenderKeySerializer,
|
||||
stateSerializer SenderKeyStateSerializer) (*SenderKey, error) {
|
||||
|
||||
// Build our sender key states from structure.
|
||||
senderKeyStates := make([]*SenderKeyState, len(structure.SenderKeyStates))
|
||||
for i := range structure.SenderKeyStates {
|
||||
var err error
|
||||
senderKeyStates[i], err = NewSenderKeyStateFromStructure(structure.SenderKeyStates[i], stateSerializer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Build and return our session.
|
||||
senderKey := &SenderKey{
|
||||
senderKeyStates: senderKeyStates,
|
||||
serializer: serializer,
|
||||
}
|
||||
|
||||
return senderKey, nil
|
||||
|
||||
}
|
||||
|
||||
// NewSenderKey record returns a new sender key record that can
|
||||
// be stored in a SenderKeyStore.
|
||||
func NewSenderKey(serializer SenderKeySerializer,
|
||||
stateSerializer SenderKeyStateSerializer) *SenderKey {
|
||||
|
||||
return &SenderKey{
|
||||
senderKeyStates: []*SenderKeyState{},
|
||||
serializer: serializer,
|
||||
stateSerializer: stateSerializer,
|
||||
}
|
||||
}
|
||||
|
||||
// SenderKeyStructure is a structure for serializing SenderKey records.
|
||||
type SenderKeyStructure struct {
|
||||
SenderKeyStates []*SenderKeyStateStructure
|
||||
}
|
||||
|
||||
// SenderKey record is a structure for storing pre keys inside
|
||||
// a SenderKeyStore.
|
||||
type SenderKey struct {
|
||||
senderKeyStates []*SenderKeyState
|
||||
serializer SenderKeySerializer
|
||||
stateSerializer SenderKeyStateSerializer
|
||||
}
|
||||
|
||||
// SenderKeyState will return the first sender key state in the record's
|
||||
// list of sender key states.
|
||||
func (k *SenderKey) SenderKeyState() (*SenderKeyState, error) {
|
||||
if len(k.senderKeyStates) > 0 {
|
||||
return k.senderKeyStates[0], nil
|
||||
}
|
||||
return nil, signalerror.ErrNoSenderKeyStatesInRecord
|
||||
}
|
||||
|
||||
// GetSenderKeyStateByID will return the sender key state with the given
|
||||
// key id.
|
||||
func (k *SenderKey) GetSenderKeyStateByID(keyID uint32) (*SenderKeyState, error) {
|
||||
for i := 0; i < len(k.senderKeyStates); i++ {
|
||||
if k.senderKeyStates[i].KeyID() == keyID {
|
||||
return k.senderKeyStates[i], nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("%w %d", signalerror.ErrNoSenderKeyStateForID, keyID)
|
||||
}
|
||||
|
||||
// IsEmpty will return false if there is more than one state in this
|
||||
// senderkey record.
|
||||
func (k *SenderKey) IsEmpty() bool {
|
||||
return len(k.senderKeyStates) == 0
|
||||
}
|
||||
|
||||
// AddSenderKeyState will add a new state to this senderkey record with the given
|
||||
// id, iteration, chainkey, and signature key.
|
||||
func (k *SenderKey) AddSenderKeyState(id uint32, iteration uint32,
|
||||
chainKey []byte, signatureKey ecc.ECPublicKeyable) {
|
||||
|
||||
newState := NewSenderKeyStateFromPublicKey(id, iteration, chainKey, signatureKey, k.stateSerializer)
|
||||
k.senderKeyStates = append([]*SenderKeyState{newState}, k.senderKeyStates...)
|
||||
|
||||
if len(k.senderKeyStates) > maxStates {
|
||||
k.senderKeyStates = k.senderKeyStates[:len(k.senderKeyStates)-1]
|
||||
}
|
||||
}
|
||||
|
||||
// SetSenderKeyState will replace the current senderkey states with the given
|
||||
// senderkey state.
|
||||
func (k *SenderKey) SetSenderKeyState(id uint32, iteration uint32,
|
||||
chainKey []byte, signatureKey *ecc.ECKeyPair) {
|
||||
|
||||
newState := NewSenderKeyState(id, iteration, chainKey, signatureKey, k.stateSerializer)
|
||||
k.senderKeyStates = make([]*SenderKeyState, 0, maxStates/2)
|
||||
k.senderKeyStates = append(k.senderKeyStates, newState)
|
||||
}
|
||||
|
||||
// Serialize will return the record as serialized bytes so it can be
|
||||
// persistently stored.
|
||||
func (k *SenderKey) Serialize() []byte {
|
||||
return k.serializer.Serialize(k.Structure())
|
||||
}
|
||||
|
||||
// Structure will return a simple serializable record structure.
|
||||
// This is used for serialization to persistently
|
||||
// store a session record.
|
||||
func (k *SenderKey) Structure() *SenderKeyStructure {
|
||||
senderKeyStates := make([]*SenderKeyStateStructure, len(k.senderKeyStates))
|
||||
for i := range k.senderKeyStates {
|
||||
senderKeyStates[i] = k.senderKeyStates[i].structure()
|
||||
}
|
||||
return &SenderKeyStructure{
|
||||
SenderKeyStates: senderKeyStates,
|
||||
}
|
||||
}
|
||||
186
vendor/go.mau.fi/libsignal/groups/state/record/SenderKeyState.go
vendored
Normal file
186
vendor/go.mau.fi/libsignal/groups/state/record/SenderKeyState.go
vendored
Normal file
@@ -0,0 +1,186 @@
|
||||
package record
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/groups/ratchet"
|
||||
"go.mau.fi/libsignal/util/bytehelper"
|
||||
)
|
||||
|
||||
const maxMessageKeys = 2000
|
||||
|
||||
// SenderKeyStateSerializer is an interface for serializing and deserializing
|
||||
// a Signal State into bytes. An implementation of this interface should be
|
||||
// used to encode/decode the object into JSON, Protobuffers, etc.
|
||||
type SenderKeyStateSerializer interface {
|
||||
Serialize(state *SenderKeyStateStructure) []byte
|
||||
Deserialize(serialized []byte) (*SenderKeyStateStructure, error)
|
||||
}
|
||||
|
||||
// NewSenderKeyStateFromBytes will return a Signal State from the given
|
||||
// bytes using the given serializer.
|
||||
func NewSenderKeyStateFromBytes(serialized []byte, serializer SenderKeyStateSerializer) (*SenderKeyState, error) {
|
||||
// Use the given serializer to decode the signal message.
|
||||
stateStructure, err := serializer.Deserialize(serialized)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewSenderKeyStateFromStructure(stateStructure, serializer)
|
||||
}
|
||||
|
||||
// NewSenderKeyState returns a new SenderKeyState.
|
||||
func NewSenderKeyState(keyID uint32, iteration uint32, chainKey []byte,
|
||||
signatureKey *ecc.ECKeyPair, serializer SenderKeyStateSerializer) *SenderKeyState {
|
||||
|
||||
return &SenderKeyState{
|
||||
keys: make([]*ratchet.SenderMessageKey, 0, maxMessageKeys/2),
|
||||
keyID: keyID,
|
||||
senderChainKey: ratchet.NewSenderChainKey(iteration, chainKey),
|
||||
signingKeyPair: signatureKey,
|
||||
serializer: serializer,
|
||||
}
|
||||
}
|
||||
|
||||
// NewSenderKeyStateFromPublicKey returns a new SenderKeyState with the given publicKey.
|
||||
func NewSenderKeyStateFromPublicKey(keyID uint32, iteration uint32, chainKey []byte,
|
||||
signatureKey ecc.ECPublicKeyable, serializer SenderKeyStateSerializer) *SenderKeyState {
|
||||
|
||||
keyPair := ecc.NewECKeyPair(signatureKey, nil)
|
||||
|
||||
return &SenderKeyState{
|
||||
keys: make([]*ratchet.SenderMessageKey, 0, maxMessageKeys/2),
|
||||
keyID: keyID,
|
||||
senderChainKey: ratchet.NewSenderChainKey(iteration, chainKey),
|
||||
signingKeyPair: keyPair,
|
||||
serializer: serializer,
|
||||
}
|
||||
}
|
||||
|
||||
// NewSenderKeyStateFromStructure will return a new session state with the
|
||||
// given state structure. This structure is given back from an
|
||||
// implementation of the sender key state serializer.
|
||||
func NewSenderKeyStateFromStructure(structure *SenderKeyStateStructure,
|
||||
serializer SenderKeyStateSerializer) (*SenderKeyState, error) {
|
||||
|
||||
// Convert our ecc keys from bytes into object form.
|
||||
signingKeyPublic, err := ecc.DecodePoint(structure.SigningKeyPublic, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signingKeyPrivate := ecc.NewDjbECPrivateKey(bytehelper.SliceToArray(structure.SigningKeyPrivate))
|
||||
|
||||
// Build our sender message keys from structure
|
||||
senderMessageKeys := make([]*ratchet.SenderMessageKey, len(structure.Keys))
|
||||
for i := range structure.Keys {
|
||||
senderMessageKeys[i] = ratchet.NewSenderMessageKeyFromStruct(structure.Keys[i])
|
||||
}
|
||||
|
||||
// Build our state object.
|
||||
state := &SenderKeyState{
|
||||
keys: senderMessageKeys,
|
||||
keyID: structure.KeyID,
|
||||
senderChainKey: ratchet.NewSenderChainKeyFromStruct(structure.SenderChainKey),
|
||||
signingKeyPair: ecc.NewECKeyPair(signingKeyPublic, signingKeyPrivate),
|
||||
serializer: serializer,
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// SenderKeyStateStructure is a serializeable structure of SenderKeyState.
|
||||
type SenderKeyStateStructure struct {
|
||||
Keys []*ratchet.SenderMessageKeyStructure
|
||||
KeyID uint32
|
||||
SenderChainKey *ratchet.SenderChainKeyStructure
|
||||
SigningKeyPrivate []byte
|
||||
SigningKeyPublic []byte
|
||||
}
|
||||
|
||||
// SenderKeyState is a structure for maintaining a senderkey session state.
|
||||
type SenderKeyState struct {
|
||||
keys []*ratchet.SenderMessageKey
|
||||
keyID uint32
|
||||
senderChainKey *ratchet.SenderChainKey
|
||||
signingKeyPair *ecc.ECKeyPair
|
||||
serializer SenderKeyStateSerializer
|
||||
}
|
||||
|
||||
// SigningKey returns the signing key pair of the sender key state.
|
||||
func (k *SenderKeyState) SigningKey() *ecc.ECKeyPair {
|
||||
return k.signingKeyPair
|
||||
}
|
||||
|
||||
// SenderChainKey returns the sender chain key of the state.
|
||||
func (k *SenderKeyState) SenderChainKey() *ratchet.SenderChainKey {
|
||||
return k.senderChainKey
|
||||
}
|
||||
|
||||
// KeyID returns the state's key id.
|
||||
func (k *SenderKeyState) KeyID() uint32 {
|
||||
return k.keyID
|
||||
}
|
||||
|
||||
// HasSenderMessageKey will return true if the state has a key with the
|
||||
// given iteration.
|
||||
func (k *SenderKeyState) HasSenderMessageKey(iteration uint32) bool {
|
||||
for i := 0; i < len(k.keys); i++ {
|
||||
if k.keys[i].Iteration() == iteration {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AddSenderMessageKey will add the given sender message key to the state.
|
||||
func (k *SenderKeyState) AddSenderMessageKey(senderMsgKey *ratchet.SenderMessageKey) {
|
||||
k.keys = append(k.keys, senderMsgKey)
|
||||
|
||||
if len(k.keys) > maxMessageKeys {
|
||||
k.keys = k.keys[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// SetSenderChainKey will set the state's sender chain key with the given key.
|
||||
func (k *SenderKeyState) SetSenderChainKey(senderChainKey *ratchet.SenderChainKey) {
|
||||
k.senderChainKey = senderChainKey
|
||||
}
|
||||
|
||||
// RemoveSenderMessageKey will remove the key in this state with the given iteration number.
|
||||
func (k *SenderKeyState) RemoveSenderMessageKey(iteration uint32) *ratchet.SenderMessageKey {
|
||||
for i := 0; i < len(k.keys); i++ {
|
||||
if k.keys[i].Iteration() == iteration {
|
||||
removed := k.keys[i]
|
||||
k.keys = append(k.keys[0:i], k.keys[i+1:]...)
|
||||
return removed
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Serialize will return the state as bytes using the given serializer.
|
||||
func (k *SenderKeyState) Serialize() []byte {
|
||||
return k.serializer.Serialize(k.structure())
|
||||
}
|
||||
|
||||
// structure will return a serializable structure of the
|
||||
// the given state so it can be persistently stored.
|
||||
func (k *SenderKeyState) structure() *SenderKeyStateStructure {
|
||||
// Convert our sender message keys into a serializeable structure
|
||||
keys := make([]*ratchet.SenderMessageKeyStructure, len(k.keys))
|
||||
for i := range k.keys {
|
||||
keys[i] = ratchet.NewStructFromSenderMessageKey(k.keys[i])
|
||||
}
|
||||
|
||||
// Build and return our state structure.
|
||||
s := &SenderKeyStateStructure{
|
||||
Keys: keys,
|
||||
KeyID: k.keyID,
|
||||
SenderChainKey: ratchet.NewStructFromSenderChainKey(k.senderChainKey),
|
||||
SigningKeyPublic: k.signingKeyPair.PublicKey().Serialize(),
|
||||
}
|
||||
if k.signingKeyPair.PrivateKey() != nil {
|
||||
s.SigningKeyPrivate = bytehelper.ArrayToSlice(k.signingKeyPair.PrivateKey().Serialize())
|
||||
}
|
||||
return s
|
||||
}
|
||||
3
vendor/go.mau.fi/libsignal/groups/state/store/Doc.go
vendored
Normal file
3
vendor/go.mau.fi/libsignal/groups/state/store/Doc.go
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package store provides the storage interfaces for storing group sender
|
||||
// key records.
|
||||
package store
|
||||
11
vendor/go.mau.fi/libsignal/groups/state/store/SenderKeyStore.go
vendored
Normal file
11
vendor/go.mau.fi/libsignal/groups/state/store/SenderKeyStore.go
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/groups/state/record"
|
||||
"go.mau.fi/libsignal/protocol"
|
||||
)
|
||||
|
||||
type SenderKey interface {
|
||||
StoreSenderKey(senderKeyName *protocol.SenderKeyName, keyRecord *record.SenderKey)
|
||||
LoadSenderKey(senderKeyName *protocol.SenderKeyName) *record.SenderKey
|
||||
}
|
||||
47
vendor/go.mau.fi/libsignal/kdf/HKDF.go
vendored
Normal file
47
vendor/go.mau.fi/libsignal/kdf/HKDF.go
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// Package kdf provides a key derivation function to calculate key output
|
||||
// and negotiate shared secrets for curve X25519 keys.
|
||||
package kdf
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/curve25519"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
|
||||
// HKDF is a hashed key derivation function type that can be used to derive keys.
|
||||
type HKDF func(inputKeyMaterial, salt, info []byte, outputLength int) ([]byte, error)
|
||||
|
||||
// DeriveSecrets derives the requested number of bytes using HKDF with the given
|
||||
// input, salt, and info.
|
||||
func DeriveSecrets(inputKeyMaterial, salt, info []byte, outputLength int) ([]byte, error) {
|
||||
kdf := hkdf.New(sha256.New, inputKeyMaterial, salt, info)
|
||||
|
||||
secrets := make([]byte, outputLength)
|
||||
length, err := io.ReadFull(kdf, secrets)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if length != outputLength {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return secrets, nil
|
||||
}
|
||||
|
||||
// CalculateSharedSecret uses DH Curve25519 to find a shared secret. The result of this function
|
||||
// should be used in `DeriveSecrets` to output the Root and Chain keys.
|
||||
func CalculateSharedSecret(theirKey, ourKey [32]byte) [32]byte {
|
||||
var sharedSecret [32]byte
|
||||
curve25519.ScalarMult(&sharedSecret, &ourKey, &theirKey)
|
||||
|
||||
return sharedSecret
|
||||
}
|
||||
|
||||
// KeyMaterial is a structure for representing a cipherkey, mac, and iv
|
||||
type KeyMaterial struct {
|
||||
CipherKey []byte
|
||||
MacKey []byte
|
||||
IV []byte
|
||||
}
|
||||
127
vendor/go.mau.fi/libsignal/keys/chain/ChainKey.go
vendored
Normal file
127
vendor/go.mau.fi/libsignal/keys/chain/ChainKey.go
vendored
Normal file
@@ -0,0 +1,127 @@
|
||||
// Package chain provides chain keys used in double ratchet sessions.
|
||||
package chain
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"go.mau.fi/libsignal/kdf"
|
||||
"go.mau.fi/libsignal/keys/message"
|
||||
)
|
||||
|
||||
var messageKeySeed = []byte{0x01}
|
||||
var chainKeySeed = []byte{0x02}
|
||||
|
||||
// NewKey returns a new chain key with the given kdf, key, and index
|
||||
func NewKey(kdf kdf.HKDF, key []byte, index uint32) *Key {
|
||||
chainKey := Key{
|
||||
kdf: kdf,
|
||||
key: key,
|
||||
index: index,
|
||||
}
|
||||
|
||||
return &chainKey
|
||||
}
|
||||
|
||||
// NewKeyFromStruct will return a chain key built from the given structure.
|
||||
func NewKeyFromStruct(structure *KeyStructure, kdf kdf.HKDF) *Key {
|
||||
return NewKey(
|
||||
kdf,
|
||||
structure.Key,
|
||||
structure.Index,
|
||||
)
|
||||
}
|
||||
|
||||
// NewStructFromKey will return a chain key structure for serialization.
|
||||
func NewStructFromKey(key *Key) *KeyStructure {
|
||||
return &KeyStructure{
|
||||
Key: key.key,
|
||||
Index: key.index,
|
||||
}
|
||||
}
|
||||
|
||||
// KeyStructure is a serializeable structure for chain keys.
|
||||
type KeyStructure struct {
|
||||
Key []byte
|
||||
Index uint32
|
||||
}
|
||||
|
||||
// Key is used for generating message keys. This key "ratchets" every time a
|
||||
// message key is generated. Every time the chain key ratchets forward, its index
|
||||
// increases by one.
|
||||
type Key struct {
|
||||
kdf kdf.HKDF
|
||||
key []byte
|
||||
index uint32 // Index's maximum size: 4,294,967,295
|
||||
}
|
||||
|
||||
// Current returns the current ChainKey struct.
|
||||
func (c *Key) Current() *Key {
|
||||
return c
|
||||
}
|
||||
|
||||
// Key returns the ChainKey's key material.
|
||||
func (c *Key) Key() []byte {
|
||||
return c.key
|
||||
}
|
||||
|
||||
// SetKey will set the ChainKey's key material.
|
||||
func (c *Key) SetKey(key []byte) {
|
||||
c.key = key
|
||||
}
|
||||
|
||||
// Index returns how many times the ChainKey has been "ratcheted" forward.
|
||||
func (c *Key) Index() uint32 {
|
||||
return c.index
|
||||
}
|
||||
|
||||
// SetIndex sets how many times the ChainKey has been "ratcheted" forward.
|
||||
func (c *Key) SetIndex(index uint32) {
|
||||
c.index = index
|
||||
}
|
||||
|
||||
// NextKey uses the key derivation function to generate a new ChainKey.
|
||||
func (c *Key) NextKey() *Key {
|
||||
nextKey := c.BaseMaterial(chainKeySeed)
|
||||
return NewKey(c.kdf, nextKey, c.index+1)
|
||||
}
|
||||
|
||||
// MessageKeys returns message keys, which includes the cipherkey, mac, iv, and index.
|
||||
func (c *Key) MessageKeys() *message.Keys {
|
||||
inputKeyMaterial := c.BaseMaterial(messageKeySeed)
|
||||
keyMaterialBytes, _ := c.kdf(inputKeyMaterial, nil, []byte(message.KdfSalt), message.DerivedSecretsSize)
|
||||
keyMaterial := newKeyMaterial(keyMaterialBytes)
|
||||
|
||||
// Use the key material returned from the key derivation function for our cipherkey, mac, and iv.
|
||||
messageKeys := message.NewKeys(
|
||||
keyMaterial.CipherKey, // Use the first 32 bytes of the key material for the CipherKey
|
||||
keyMaterial.MacKey, // Use bytes 32-64 of the key material for the MacKey
|
||||
keyMaterial.IV, // Use the last 16 bytes for the IV.
|
||||
c.Index(), // Attach the chain key's index to the message keys.
|
||||
)
|
||||
|
||||
return messageKeys
|
||||
}
|
||||
|
||||
// BaseMaterial uses hmac to derive the base material used in the key derivation function for a new key.
|
||||
func (c *Key) BaseMaterial(seed []byte) []byte {
|
||||
mac := hmac.New(sha256.New, c.key[:])
|
||||
mac.Write(seed)
|
||||
|
||||
return mac.Sum(nil)
|
||||
}
|
||||
|
||||
// NewKeyMaterial takes an 80-byte slice derived from a key derivation function and splits
|
||||
// it into the cipherkey, mac, and iv.
|
||||
func newKeyMaterial(keyMaterialBytes []byte) *kdf.KeyMaterial {
|
||||
cipherKey := keyMaterialBytes[:32] // Use the first 32 bytes of the key material for the CipherKey
|
||||
macKey := keyMaterialBytes[32:64] // Use bytes 32-64 of the key material for the MacKey
|
||||
iv := keyMaterialBytes[64:80] // Use the last 16 bytes for the IV.
|
||||
|
||||
keyMaterial := kdf.KeyMaterial{
|
||||
CipherKey: cipherKey,
|
||||
MacKey: macKey,
|
||||
IV: iv,
|
||||
}
|
||||
|
||||
return &keyMaterial
|
||||
}
|
||||
47
vendor/go.mau.fi/libsignal/keys/identity/IdentityKey.go
vendored
Normal file
47
vendor/go.mau.fi/libsignal/keys/identity/IdentityKey.go
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
// Package identity provides identity keys used for verifying the identity
|
||||
// of a signal user.
|
||||
package identity
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
)
|
||||
|
||||
// NewKey generates a new IdentityKey from an ECPublicKey
|
||||
func NewKey(publicKey ecc.ECPublicKeyable) *Key {
|
||||
identityKey := Key{
|
||||
publicKey: publicKey,
|
||||
}
|
||||
|
||||
return &identityKey
|
||||
}
|
||||
|
||||
// NewKeyFromBytes generates a new IdentityKey from public key bytes
|
||||
func NewKeyFromBytes(publicKey [32]byte, offset int) Key {
|
||||
identityKey := Key{
|
||||
publicKey: ecc.NewDjbECPublicKey(publicKey),
|
||||
}
|
||||
|
||||
return identityKey
|
||||
}
|
||||
|
||||
// Key is a structure for representing an identity key. This same structure can
|
||||
// be used for verifying recipient's identity key or storing our own identity key.
|
||||
type Key struct {
|
||||
publicKey ecc.ECPublicKeyable
|
||||
}
|
||||
|
||||
// Fingerprint gets the string fingerprint representation of the public key.
|
||||
func (k *Key) Fingerprint() string {
|
||||
return hex.EncodeToString(k.publicKey.Serialize())
|
||||
}
|
||||
|
||||
// PublicKey returns the EC Public key of the identity key
|
||||
func (k *Key) PublicKey() ecc.ECPublicKeyable {
|
||||
return k.publicKey
|
||||
}
|
||||
|
||||
// Serialize returns the serialized version of the key
|
||||
func (k *Key) Serialize() []byte {
|
||||
return k.publicKey.Serialize()
|
||||
}
|
||||
39
vendor/go.mau.fi/libsignal/keys/identity/IdentityKeyPair.go
vendored
Normal file
39
vendor/go.mau.fi/libsignal/keys/identity/IdentityKeyPair.go
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
package identity
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
)
|
||||
|
||||
// NewKeyPair returns a new identity key with the given public and private keys.
|
||||
func NewKeyPair(publicKey *Key, privateKey ecc.ECPrivateKeyable) *KeyPair {
|
||||
keyPair := KeyPair{
|
||||
publicKey: publicKey,
|
||||
privateKey: privateKey,
|
||||
}
|
||||
|
||||
return &keyPair
|
||||
}
|
||||
|
||||
// NewKeyPairFromBytes returns a new identity key from the given serialized bytes.
|
||||
//func NewKeyPairFromBytes(serialized []byte) KeyPair {
|
||||
//}
|
||||
|
||||
// KeyPair is a holder for public and private identity key pair.
|
||||
type KeyPair struct {
|
||||
publicKey *Key
|
||||
privateKey ecc.ECPrivateKeyable
|
||||
}
|
||||
|
||||
// PublicKey returns the identity key's public key.
|
||||
func (k *KeyPair) PublicKey() *Key {
|
||||
return k.publicKey
|
||||
}
|
||||
|
||||
// PrivateKey returns the identity key's private key.
|
||||
func (k *KeyPair) PrivateKey() ecc.ECPrivateKeyable {
|
||||
return k.privateKey
|
||||
}
|
||||
|
||||
// Serialize returns a byte array that represents the keypair.
|
||||
//func (k *KeyPair) Serialize() []byte {
|
||||
//}
|
||||
91
vendor/go.mau.fi/libsignal/keys/message/MessageKey.go
vendored
Normal file
91
vendor/go.mau.fi/libsignal/keys/message/MessageKey.go
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
// Package message provides a structure for message keys, which are symmetric
|
||||
// keys used for the encryption/decryption of Signal messages.
|
||||
package message
|
||||
|
||||
// DerivedSecretsSize is the size of the derived secrets for message keys.
|
||||
const DerivedSecretsSize = 80
|
||||
|
||||
// CipherKeyLength is the length of the actual cipher key used for messages.
|
||||
const CipherKeyLength = 32
|
||||
|
||||
// MacKeyLength is the length of the message authentication code in bytes.
|
||||
const MacKeyLength = 32
|
||||
|
||||
// IVLength is the length of the initialization vector in bytes.
|
||||
const IVLength = 16
|
||||
|
||||
// KdfSalt is used as the Salt for message keys to derive secrets using a Key Derivation Function
|
||||
const KdfSalt string = "WhisperMessageKeys"
|
||||
|
||||
// NewKeys returns a new message keys structure with the given cipherKey, mac, iv, and index.
|
||||
func NewKeys(cipherKey, macKey, iv []byte, index uint32) *Keys {
|
||||
messageKeys := Keys{
|
||||
cipherKey: cipherKey,
|
||||
macKey: macKey,
|
||||
iv: iv,
|
||||
index: index,
|
||||
}
|
||||
|
||||
return &messageKeys
|
||||
}
|
||||
|
||||
// NewKeysFromStruct will return a new message keys object from the
|
||||
// given serializeable structure.
|
||||
func NewKeysFromStruct(structure *KeysStructure) *Keys {
|
||||
return NewKeys(
|
||||
structure.CipherKey,
|
||||
structure.MacKey,
|
||||
structure.IV,
|
||||
structure.Index,
|
||||
)
|
||||
}
|
||||
|
||||
// NewStructFromKeys returns a serializeable structure of message keys.
|
||||
func NewStructFromKeys(keys *Keys) *KeysStructure {
|
||||
return &KeysStructure{
|
||||
CipherKey: keys.cipherKey,
|
||||
MacKey: keys.macKey,
|
||||
IV: keys.iv,
|
||||
Index: keys.index,
|
||||
}
|
||||
}
|
||||
|
||||
// KeysStructure is a serializeable structure of message keys.
|
||||
type KeysStructure struct {
|
||||
CipherKey []byte
|
||||
MacKey []byte
|
||||
IV []byte
|
||||
Index uint32
|
||||
}
|
||||
|
||||
// Keys is a structure to hold all the keys for a single MessageKey, including the
|
||||
// cipherKey, mac, iv, and index of the chain key. MessageKeys are used to
|
||||
// encrypt individual messages.
|
||||
type Keys struct {
|
||||
cipherKey []byte
|
||||
macKey []byte
|
||||
iv []byte
|
||||
index uint32
|
||||
}
|
||||
|
||||
// CipherKey is the key used to produce ciphertext.
|
||||
func (k *Keys) CipherKey() []byte {
|
||||
return k.cipherKey
|
||||
}
|
||||
|
||||
// MacKey returns the message's message authentication code.
|
||||
func (k *Keys) MacKey() []byte {
|
||||
return k.macKey
|
||||
}
|
||||
|
||||
// Iv returns the message keys' initialization vector. The IV is a fixed-size input
|
||||
// to a cryptographic primitive.
|
||||
func (k *Keys) Iv() []byte {
|
||||
return k.iv
|
||||
}
|
||||
|
||||
// Index returns the number of times the chain key has been put through a key derivation
|
||||
// function to generate this message key.
|
||||
func (k *Keys) Index() uint32 {
|
||||
return k.index
|
||||
}
|
||||
86
vendor/go.mau.fi/libsignal/keys/prekey/PreKeyBundle.go
vendored
Normal file
86
vendor/go.mau.fi/libsignal/keys/prekey/PreKeyBundle.go
vendored
Normal file
@@ -0,0 +1,86 @@
|
||||
// Package prekey provides prekey bundle structures for calculating
|
||||
// a new Signal session with a user asyncronously.
|
||||
package prekey
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/keys/identity"
|
||||
"go.mau.fi/libsignal/util/optional"
|
||||
)
|
||||
|
||||
// NewBundle returns a Bundle structure that contains a remote PreKey
|
||||
// and collection of associated items.
|
||||
func NewBundle(registrationID, deviceID uint32, preKeyID *optional.Uint32, signedPreKeyID uint32,
|
||||
preKeyPublic, signedPreKeyPublic ecc.ECPublicKeyable, signedPreKeySig [64]byte,
|
||||
identityKey *identity.Key) *Bundle {
|
||||
|
||||
bundle := Bundle{
|
||||
registrationID: registrationID,
|
||||
deviceID: deviceID,
|
||||
preKeyID: preKeyID,
|
||||
preKeyPublic: preKeyPublic,
|
||||
signedPreKeyID: signedPreKeyID,
|
||||
signedPreKeyPublic: signedPreKeyPublic,
|
||||
signedPreKeySignature: signedPreKeySig,
|
||||
identityKey: identityKey,
|
||||
}
|
||||
|
||||
return &bundle
|
||||
}
|
||||
|
||||
// Bundle is a structure that contains a remote PreKey and collection
|
||||
// of associated items.
|
||||
type Bundle struct {
|
||||
registrationID uint32
|
||||
deviceID uint32
|
||||
preKeyID *optional.Uint32
|
||||
preKeyPublic ecc.ECPublicKeyable
|
||||
signedPreKeyID uint32
|
||||
signedPreKeyPublic ecc.ECPublicKeyable
|
||||
signedPreKeySignature [64]byte
|
||||
identityKey *identity.Key
|
||||
}
|
||||
|
||||
// DeviceID returns the device ID this PreKey belongs to.
|
||||
func (b *Bundle) DeviceID() uint32 {
|
||||
return b.deviceID
|
||||
}
|
||||
|
||||
// PreKeyID returns the unique key ID for this PreKey.
|
||||
func (b *Bundle) PreKeyID() *optional.Uint32 {
|
||||
return b.preKeyID
|
||||
}
|
||||
|
||||
// PreKey returns the public key for this PreKey.
|
||||
func (b *Bundle) PreKey() ecc.ECPublicKeyable {
|
||||
return b.preKeyPublic
|
||||
}
|
||||
|
||||
// SignedPreKeyID returns the unique key ID for this
|
||||
// signed PreKey.
|
||||
func (b *Bundle) SignedPreKeyID() uint32 {
|
||||
return b.signedPreKeyID
|
||||
}
|
||||
|
||||
// SignedPreKey returns the signed PreKey for this
|
||||
// PreKeyBundle.
|
||||
func (b *Bundle) SignedPreKey() ecc.ECPublicKeyable {
|
||||
return b.signedPreKeyPublic
|
||||
}
|
||||
|
||||
// SignedPreKeySignature returns the signature over the
|
||||
// signed PreKey.
|
||||
func (b *Bundle) SignedPreKeySignature() [64]byte {
|
||||
return b.signedPreKeySignature
|
||||
}
|
||||
|
||||
// IdentityKey returns the Identity Key of this PreKey's owner.
|
||||
func (b *Bundle) IdentityKey() *identity.Key {
|
||||
return b.identityKey
|
||||
}
|
||||
|
||||
// RegistrationID returns the registration ID associated with
|
||||
// this PreKey.
|
||||
func (b *Bundle) RegistrationID() uint32 {
|
||||
return b.registrationID
|
||||
}
|
||||
66
vendor/go.mau.fi/libsignal/keys/root/RootKey.go
vendored
Normal file
66
vendor/go.mau.fi/libsignal/keys/root/RootKey.go
vendored
Normal file
@@ -0,0 +1,66 @@
|
||||
// Package root provides root keys which are used to derive new chain and
|
||||
// root keys in a ratcheting session.
|
||||
package root
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/kdf"
|
||||
"go.mau.fi/libsignal/keys/chain"
|
||||
"go.mau.fi/libsignal/keys/session"
|
||||
)
|
||||
|
||||
// DerivedSecretsSize is the size of the derived secrets for root keys.
|
||||
const DerivedSecretsSize = 64
|
||||
|
||||
// KdfInfo is used as the info for message keys to derive secrets using a Key Derivation Function
|
||||
const KdfInfo string = "WhisperRatchet"
|
||||
|
||||
// NewKey returns a new RootKey given the key derivation function and bytes.
|
||||
func NewKey(kdf kdf.HKDF, key []byte) *Key {
|
||||
rootKey := Key{
|
||||
kdf: kdf,
|
||||
key: key,
|
||||
}
|
||||
|
||||
return &rootKey
|
||||
}
|
||||
|
||||
// Key is a structure for RootKeys, which are used to derive a new set of chain and root
|
||||
// keys for every round trip of messages.
|
||||
type Key struct {
|
||||
kdf kdf.HKDF
|
||||
key []byte
|
||||
}
|
||||
|
||||
// Bytes returns the RootKey in bytes.
|
||||
func (k *Key) Bytes() []byte {
|
||||
return k.key
|
||||
}
|
||||
|
||||
// CreateChain creates a new RootKey and ChainKey from the recipient's ratchet key and our private key.
|
||||
func (k *Key) CreateChain(theirRatchetKey ecc.ECPublicKeyable, ourRatchetKey *ecc.ECKeyPair) (*session.KeyPair, error) {
|
||||
theirPublicKey := theirRatchetKey.PublicKey()
|
||||
ourPrivateKey := ourRatchetKey.PrivateKey().Serialize()
|
||||
|
||||
// Use our key derivation function to calculate a shared secret.
|
||||
sharedSecret := kdf.CalculateSharedSecret(theirPublicKey, ourPrivateKey)
|
||||
derivedSecretBytes, err := kdf.DeriveSecrets(sharedSecret[:], k.key, []byte(KdfInfo), DerivedSecretsSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Split the derived secret bytes in half, using one half for the root key and the second for the chain key.
|
||||
derivedSecrets := session.NewDerivedSecrets(derivedSecretBytes)
|
||||
|
||||
// Create new root and chain key structures from the derived secrets.
|
||||
rootKey := NewKey(k.kdf, derivedSecrets.RootKey())
|
||||
chainKey := chain.NewKey(k.kdf, derivedSecrets.ChainKey(), 0)
|
||||
|
||||
// Create a session keypair with the generated root and chain keys.
|
||||
keyPair := session.NewKeyPair(
|
||||
rootKey,
|
||||
chainKey,
|
||||
)
|
||||
|
||||
return keyPair, nil
|
||||
}
|
||||
29
vendor/go.mau.fi/libsignal/keys/session/DerivedSecrets.go
vendored
Normal file
29
vendor/go.mau.fi/libsignal/keys/session/DerivedSecrets.go
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
package session
|
||||
|
||||
// NewDerivedSecrets returns a new RootKey/ChainKey pair from 64 bytes of key material
|
||||
// generated by the key derivation function.
|
||||
func NewDerivedSecrets(keyMaterial []byte) *DerivedSecrets {
|
||||
secrets := DerivedSecrets{
|
||||
keyMaterial[:32],
|
||||
keyMaterial[32:],
|
||||
}
|
||||
|
||||
return &secrets
|
||||
}
|
||||
|
||||
// DerivedSecrets is a structure for holding the derived secrets for the
|
||||
// Root and Chain keys for a session.
|
||||
type DerivedSecrets struct {
|
||||
rootKey []byte
|
||||
chainKey []byte
|
||||
}
|
||||
|
||||
// RootKey returns the RootKey bytes.
|
||||
func (d *DerivedSecrets) RootKey() []byte {
|
||||
return d.rootKey
|
||||
}
|
||||
|
||||
// ChainKey returns the ChainKey bytes.
|
||||
func (d *DerivedSecrets) ChainKey() []byte {
|
||||
return d.chainKey
|
||||
}
|
||||
43
vendor/go.mau.fi/libsignal/keys/session/Pair.go
vendored
Normal file
43
vendor/go.mau.fi/libsignal/keys/session/Pair.go
vendored
Normal file
@@ -0,0 +1,43 @@
|
||||
// Package session provides a simple structure for session keys, which is
|
||||
// a pair of root and chain keys for a session.
|
||||
package session
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/keys/chain"
|
||||
"go.mau.fi/libsignal/keys/message"
|
||||
)
|
||||
|
||||
// RootKeyable is an interface for all root key implementations that are part of
|
||||
// a session keypair.
|
||||
type RootKeyable interface {
|
||||
Bytes() []byte
|
||||
CreateChain(theirRatchetKey ecc.ECPublicKeyable, ourRatchetKey *ecc.ECKeyPair) (*KeyPair, error)
|
||||
}
|
||||
|
||||
// ChainKeyable is an interface for all chain key implementations that are part of
|
||||
// a session keypair.
|
||||
type ChainKeyable interface {
|
||||
Key() []byte
|
||||
Index() uint32
|
||||
NextKey() *chain.Key
|
||||
MessageKeys() *message.Keys
|
||||
Current() *chain.Key
|
||||
}
|
||||
|
||||
// NewKeyPair returns a new session key pair that holds a root and chain key.
|
||||
func NewKeyPair(rootKey RootKeyable, chainKey ChainKeyable) *KeyPair {
|
||||
keyPair := KeyPair{
|
||||
RootKey: rootKey,
|
||||
ChainKey: chainKey,
|
||||
}
|
||||
|
||||
return &keyPair
|
||||
}
|
||||
|
||||
// KeyPair is a session key pair that holds a single root and chain key pair. These
|
||||
// keys are ratcheted after every message sent and every message round trip.
|
||||
type KeyPair struct {
|
||||
RootKey RootKeyable
|
||||
ChainKey ChainKeyable
|
||||
}
|
||||
85
vendor/go.mau.fi/libsignal/logger/DefaultLogger.go
vendored
Normal file
85
vendor/go.mau.fi/libsignal/logger/DefaultLogger.go
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DefaultLogger is used if no logger has been set up.
|
||||
type defaultLogger struct {
|
||||
namespaces []string
|
||||
}
|
||||
|
||||
// log simply logs the given message to stdout if the message
|
||||
// caller is allowed to log.
|
||||
func (d *defaultLogger) log(level, caller, msg string) {
|
||||
if !d.shouldLog(caller) {
|
||||
// return
|
||||
}
|
||||
t := time.Now()
|
||||
fmt.Println(
|
||||
"["+level+"]",
|
||||
t.Format(time.RFC3339),
|
||||
caller,
|
||||
"▶ ",
|
||||
msg,
|
||||
)
|
||||
}
|
||||
|
||||
// shouldLog determines whether or not the given caller should
|
||||
// be allowed to log messages.
|
||||
func (d *defaultLogger) shouldLog(caller string) bool {
|
||||
shouldLog := false
|
||||
d.ensureNamespaces()
|
||||
for _, namespace := range d.namespaces {
|
||||
if namespace == "all" {
|
||||
shouldLog = true
|
||||
}
|
||||
if strings.Contains(caller, namespace) {
|
||||
shouldLog = true
|
||||
}
|
||||
}
|
||||
|
||||
return shouldLog
|
||||
}
|
||||
|
||||
// ensureNamespaces checks to see if our list of loggable namespaces
|
||||
// has been initialized or not. If not, it defaults to log all.
|
||||
func (d *defaultLogger) ensureNamespaces() {
|
||||
if d.namespaces == nil {
|
||||
d.namespaces = []string{"all"}
|
||||
}
|
||||
}
|
||||
|
||||
// Debug is used to log debug messages.
|
||||
func (d *defaultLogger) Debug(caller, msg string) {
|
||||
//d.log("DEBUG", caller, msg)
|
||||
}
|
||||
|
||||
// Info is used to log info messages.
|
||||
func (d *defaultLogger) Info(caller, msg string) {
|
||||
d.log("INFO", caller, msg)
|
||||
}
|
||||
|
||||
// Warning is used to log warning messages.
|
||||
func (d *defaultLogger) Warning(caller, msg string) {
|
||||
d.log("WARNING", caller, msg)
|
||||
}
|
||||
|
||||
// Error is used to log error messages.
|
||||
func (d *defaultLogger) Error(caller, msg string) {
|
||||
d.log("ERROR", caller, msg)
|
||||
}
|
||||
|
||||
// Configure takes a configuration string separated by commas
|
||||
// that contains all the callers that should be logged. This
|
||||
// allows granular logging of different go files.
|
||||
//
|
||||
// Example:
|
||||
// logger.Configure("RootKey.go,Curve.go")
|
||||
// logger.Configure("all")
|
||||
//
|
||||
func (d *defaultLogger) Configure(settings string) {
|
||||
d.namespaces = strings.Split(settings, ",")
|
||||
}
|
||||
89
vendor/go.mau.fi/libsignal/logger/Logger.go
vendored
Normal file
89
vendor/go.mau.fi/libsignal/logger/Logger.go
vendored
Normal file
@@ -0,0 +1,89 @@
|
||||
// Package logger provides optional debug logging of the Signal library.
|
||||
package logger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Logger is a shared loggable interface that this library will use for all log messages.
|
||||
var Logger Loggable
|
||||
|
||||
// Loggable is an interface for logging.
|
||||
type Loggable interface {
|
||||
Debug(caller, message string)
|
||||
Info(caller, message string)
|
||||
Warning(caller, message string)
|
||||
Error(caller, message string)
|
||||
Configure(settings string)
|
||||
}
|
||||
|
||||
// Setup will configure the shared logger to use the provided logger.
|
||||
func Setup(logger *Loggable) {
|
||||
Logger = *logger
|
||||
}
|
||||
|
||||
// ToString converts an arbitrary number of objects to a string for use in a logger.
|
||||
func toString(a ...interface{}) string {
|
||||
return fmt.Sprint(a...)
|
||||
}
|
||||
|
||||
// EnsureLogger will use the default logger if one was not set up.
|
||||
func ensureLogger() {
|
||||
if Logger == nil {
|
||||
// fmt.Println("Error: No logger was configured. Use `logger.Setup` to configure a logger.")
|
||||
Logger = &defaultLogger{}
|
||||
}
|
||||
}
|
||||
|
||||
// GetCaller gets the go file name and line number that the logger was called from.
|
||||
func getCaller() string {
|
||||
var file string
|
||||
_, path, line, _ := runtime.Caller(2)
|
||||
paths := strings.Split(path, "/")
|
||||
if len(paths) > 0 {
|
||||
file = paths[len(paths)-1]
|
||||
} else {
|
||||
file = "<unkn>"
|
||||
}
|
||||
|
||||
return file + ":" + strconv.Itoa(line)
|
||||
}
|
||||
|
||||
/*
|
||||
* Go methods used by the library for logging.
|
||||
*/
|
||||
|
||||
// Debug prints debug level logs.
|
||||
func Debug(msg ...interface{}) {
|
||||
ensureLogger()
|
||||
Logger.Debug(getCaller(), toString(msg...))
|
||||
}
|
||||
|
||||
// Info prints info level logs.
|
||||
func Info(msg ...interface{}) {
|
||||
ensureLogger()
|
||||
Logger.Info(getCaller(), toString(msg...))
|
||||
}
|
||||
|
||||
// Warning prints warning level logs.
|
||||
func Warning(msg ...interface{}) {
|
||||
ensureLogger()
|
||||
Logger.Warning(getCaller(), toString(msg...))
|
||||
}
|
||||
|
||||
// Error prints error level logs.
|
||||
func Error(msg ...interface{}) {
|
||||
ensureLogger()
|
||||
Logger.Error(getCaller(), toString(msg...))
|
||||
}
|
||||
|
||||
// Configure allows arbitrary logger configuration settings. The
|
||||
// default logger uses this method to configure what Go files
|
||||
// are allowed to log.
|
||||
func Configure(settings string) {
|
||||
ensureLogger()
|
||||
Logger.Configure(settings)
|
||||
}
|
||||
19
vendor/go.mau.fi/libsignal/protocol/CiphertextMessage.go
vendored
Normal file
19
vendor/go.mau.fi/libsignal/protocol/CiphertextMessage.go
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
package protocol
|
||||
|
||||
type CiphertextMessage interface {
|
||||
Serialize() []byte
|
||||
Type() uint32
|
||||
}
|
||||
|
||||
type GroupCiphertextMessage interface {
|
||||
CiphertextMessage
|
||||
SignedSerialize() []byte
|
||||
}
|
||||
|
||||
const UnsupportedVersion = 1
|
||||
const CurrentVersion = 3
|
||||
|
||||
const WHISPER_TYPE = 2
|
||||
const PREKEY_TYPE = 3
|
||||
const SENDERKEY_TYPE = 4
|
||||
const SENDERKEY_DISTRIBUTION_TYPE = 5
|
||||
3
vendor/go.mau.fi/libsignal/protocol/Doc.go
vendored
Normal file
3
vendor/go.mau.fi/libsignal/protocol/Doc.go
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package protocol provides address, group, and message structures that
|
||||
// the Signal protocol uses for sending encrypted messages.
|
||||
package protocol
|
||||
152
vendor/go.mau.fi/libsignal/protocol/PreKeySignalMessage.go
vendored
Normal file
152
vendor/go.mau.fi/libsignal/protocol/PreKeySignalMessage.go
vendored
Normal file
@@ -0,0 +1,152 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/keys/identity"
|
||||
"go.mau.fi/libsignal/signalerror"
|
||||
"go.mau.fi/libsignal/util/optional"
|
||||
)
|
||||
|
||||
// PreKeySignalMessageSerializer is an interface for serializing and deserializing
|
||||
// PreKeySignalMessages into bytes. An implementation of this interface should be
|
||||
// used to encode/decode the object into JSON, Protobuffers, etc.
|
||||
type PreKeySignalMessageSerializer interface {
|
||||
Serialize(signalMessage *PreKeySignalMessageStructure) []byte
|
||||
Deserialize(serialized []byte) (*PreKeySignalMessageStructure, error)
|
||||
}
|
||||
|
||||
// NewPreKeySignalMessageFromBytes will return a Signal Ciphertext message from the given
|
||||
// bytes using the given serializer.
|
||||
func NewPreKeySignalMessageFromBytes(serialized []byte, serializer PreKeySignalMessageSerializer,
|
||||
msgSerializer SignalMessageSerializer) (*PreKeySignalMessage, error) {
|
||||
// Use the given serializer to decode the signal message.
|
||||
signalMessageStructure, err := serializer.Deserialize(serialized)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewPreKeySignalMessageFromStruct(signalMessageStructure, serializer, msgSerializer)
|
||||
}
|
||||
|
||||
// NewPreKeySignalMessageFromStruct will return a new PreKeySignalMessage from the given
|
||||
// PreKeySignalMessageStructure.
|
||||
func NewPreKeySignalMessageFromStruct(structure *PreKeySignalMessageStructure,
|
||||
serializer PreKeySignalMessageSerializer, msgSerializer SignalMessageSerializer) (*PreKeySignalMessage, error) {
|
||||
|
||||
// Throw an error if the given message structure is an unsupported version.
|
||||
if structure.Version <= UnsupportedVersion {
|
||||
return nil, fmt.Errorf("%w %d (prekey message)", signalerror.ErrOldMessageVersion, structure.Version)
|
||||
}
|
||||
|
||||
// Throw an error if the given message structure is a future version.
|
||||
if structure.Version > CurrentVersion {
|
||||
return nil, fmt.Errorf("%w %d (prekey message)", signalerror.ErrUnknownMessageVersion, structure.Version)
|
||||
}
|
||||
|
||||
// Throw an error if the structure is missing critical fields.
|
||||
if structure.BaseKey == nil || structure.IdentityKey == nil || structure.Message == nil {
|
||||
return nil, fmt.Errorf("%w (prekey message)", signalerror.ErrIncompleteMessage)
|
||||
}
|
||||
|
||||
// Create the signal message object from the structure.
|
||||
preKeyWhisperMessage := &PreKeySignalMessage{structure: *structure, serializer: serializer}
|
||||
|
||||
// Generate the base ECC key from bytes.
|
||||
var err error
|
||||
preKeyWhisperMessage.baseKey, err = ecc.DecodePoint(structure.BaseKey, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Generate the identity key from bytes
|
||||
var identityKey ecc.ECPublicKeyable
|
||||
identityKey, err = ecc.DecodePoint(structure.IdentityKey, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
preKeyWhisperMessage.identityKey = identity.NewKey(identityKey)
|
||||
|
||||
// Generate the SignalMessage object from bytes.
|
||||
preKeyWhisperMessage.message, err = NewSignalMessageFromBytes(structure.Message, msgSerializer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return preKeyWhisperMessage, nil
|
||||
}
|
||||
|
||||
// NewPreKeySignalMessage will return a new PreKeySignalMessage object.
|
||||
func NewPreKeySignalMessage(version int, registrationID uint32, preKeyID *optional.Uint32, signedPreKeyID uint32,
|
||||
baseKey ecc.ECPublicKeyable, identityKey *identity.Key, message *SignalMessage, serializer PreKeySignalMessageSerializer,
|
||||
msgSerializer SignalMessageSerializer) (*PreKeySignalMessage, error) {
|
||||
structure := &PreKeySignalMessageStructure{
|
||||
Version: version,
|
||||
RegistrationID: registrationID,
|
||||
PreKeyID: preKeyID,
|
||||
SignedPreKeyID: signedPreKeyID,
|
||||
BaseKey: baseKey.Serialize(),
|
||||
IdentityKey: identityKey.PublicKey().Serialize(),
|
||||
Message: message.Serialize(),
|
||||
}
|
||||
return NewPreKeySignalMessageFromStruct(structure, serializer, msgSerializer)
|
||||
}
|
||||
|
||||
// PreKeySignalMessageStructure is a serializable structure for
|
||||
// PreKeySignalMessages.
|
||||
type PreKeySignalMessageStructure struct {
|
||||
RegistrationID uint32
|
||||
PreKeyID *optional.Uint32
|
||||
SignedPreKeyID uint32
|
||||
BaseKey []byte
|
||||
IdentityKey []byte
|
||||
Message []byte
|
||||
Version int
|
||||
}
|
||||
|
||||
// PreKeySignalMessage is an encrypted Signal message that is designed
|
||||
// to be used when building a session with someone for the first time.
|
||||
type PreKeySignalMessage struct {
|
||||
structure PreKeySignalMessageStructure
|
||||
baseKey ecc.ECPublicKeyable
|
||||
identityKey *identity.Key
|
||||
message *SignalMessage
|
||||
serializer PreKeySignalMessageSerializer
|
||||
}
|
||||
|
||||
func (p *PreKeySignalMessage) MessageVersion() int {
|
||||
return p.structure.Version
|
||||
}
|
||||
|
||||
func (p *PreKeySignalMessage) IdentityKey() *identity.Key {
|
||||
return p.identityKey
|
||||
}
|
||||
|
||||
func (p *PreKeySignalMessage) RegistrationID() uint32 {
|
||||
return p.structure.RegistrationID
|
||||
}
|
||||
|
||||
func (p *PreKeySignalMessage) PreKeyID() *optional.Uint32 {
|
||||
return p.structure.PreKeyID
|
||||
}
|
||||
|
||||
func (p *PreKeySignalMessage) SignedPreKeyID() uint32 {
|
||||
return p.structure.SignedPreKeyID
|
||||
}
|
||||
|
||||
func (p *PreKeySignalMessage) BaseKey() ecc.ECPublicKeyable {
|
||||
return p.baseKey
|
||||
}
|
||||
|
||||
func (p *PreKeySignalMessage) WhisperMessage() *SignalMessage {
|
||||
return p.message
|
||||
}
|
||||
|
||||
func (p *PreKeySignalMessage) Serialize() []byte {
|
||||
return p.serializer.Serialize(&p.structure)
|
||||
}
|
||||
|
||||
func (p *PreKeySignalMessage) Type() uint32 {
|
||||
return PREKEY_TYPE
|
||||
}
|
||||
147
vendor/go.mau.fi/libsignal/protocol/SenderKeyDistributionMessage.go
vendored
Normal file
147
vendor/go.mau.fi/libsignal/protocol/SenderKeyDistributionMessage.go
vendored
Normal file
@@ -0,0 +1,147 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/signalerror"
|
||||
)
|
||||
|
||||
// SenderKeyDistributionMessageSerializer is an interface for serializing and deserializing
|
||||
// SenderKeyDistributionMessages into bytes. An implementation of this interface should be
|
||||
// used to encode/decode the object into JSON, Protobuffers, etc.
|
||||
type SenderKeyDistributionMessageSerializer interface {
|
||||
Serialize(signalMessage *SenderKeyDistributionMessageStructure) []byte
|
||||
Deserialize(serialized []byte) (*SenderKeyDistributionMessageStructure, error)
|
||||
}
|
||||
|
||||
// NewSenderKeyDistributionMessageFromBytes will return a Signal Ciphertext message from the given
|
||||
// bytes using the given serializer.
|
||||
func NewSenderKeyDistributionMessageFromBytes(serialized []byte,
|
||||
serializer SenderKeyDistributionMessageSerializer) (*SenderKeyDistributionMessage, error) {
|
||||
|
||||
// Use the given serializer to decode the signal message.
|
||||
signalMessageStructure, err := serializer.Deserialize(serialized)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewSenderKeyDistributionMessageFromStruct(signalMessageStructure, serializer)
|
||||
}
|
||||
|
||||
// NewSenderKeyDistributionMessageFromStruct returns a Signal Ciphertext message from the
|
||||
// given serializable structure.
|
||||
func NewSenderKeyDistributionMessageFromStruct(structure *SenderKeyDistributionMessageStructure,
|
||||
serializer SenderKeyDistributionMessageSerializer) (*SenderKeyDistributionMessage, error) {
|
||||
|
||||
// Throw an error if the given message structure is an unsupported version.
|
||||
if structure.Version <= UnsupportedVersion {
|
||||
return nil, fmt.Errorf("%w %d (sender key distribution)", signalerror.ErrOldMessageVersion, structure.Version)
|
||||
}
|
||||
|
||||
// Throw an error if the given message structure is a future version.
|
||||
if structure.Version > CurrentVersion {
|
||||
return nil, fmt.Errorf("%w %d (sender key distribution)", signalerror.ErrUnknownMessageVersion, structure.Version)
|
||||
}
|
||||
|
||||
// Throw an error if the structure is missing critical fields.
|
||||
if structure.SigningKey == nil || structure.ChainKey == nil {
|
||||
return nil, fmt.Errorf("%w (sender key distribution)", signalerror.ErrIncompleteMessage)
|
||||
}
|
||||
|
||||
// Get the signing key object from bytes.
|
||||
signingKey, err := ecc.DecodePoint(structure.SigningKey, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Create the signal message object from the structure.
|
||||
message := &SenderKeyDistributionMessage{
|
||||
id: structure.ID,
|
||||
iteration: structure.Iteration,
|
||||
chainKey: structure.ChainKey,
|
||||
version: structure.Version,
|
||||
signatureKey: signingKey,
|
||||
serializer: serializer,
|
||||
}
|
||||
|
||||
// Generate the ECC key from bytes.
|
||||
message.signatureKey, err = ecc.DecodePoint(structure.SigningKey, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return message, nil
|
||||
}
|
||||
|
||||
// NewSenderKeyDistributionMessage returns a Signal Ciphertext message.
|
||||
func NewSenderKeyDistributionMessage(id uint32, iteration uint32,
|
||||
chainKey []byte, signatureKey ecc.ECPublicKeyable,
|
||||
serializer SenderKeyDistributionMessageSerializer) *SenderKeyDistributionMessage {
|
||||
|
||||
return &SenderKeyDistributionMessage{
|
||||
id: id,
|
||||
iteration: iteration,
|
||||
chainKey: chainKey,
|
||||
signatureKey: signatureKey,
|
||||
serializer: serializer,
|
||||
}
|
||||
}
|
||||
|
||||
// SenderKeyDistributionMessageStructure is a serializeable structure for senderkey
|
||||
// distribution messages.
|
||||
type SenderKeyDistributionMessageStructure struct {
|
||||
ID uint32
|
||||
Iteration uint32
|
||||
ChainKey []byte
|
||||
SigningKey []byte
|
||||
Version uint32
|
||||
}
|
||||
|
||||
// SenderKeyDistributionMessage is a structure for senderkey distribution messages.
|
||||
type SenderKeyDistributionMessage struct {
|
||||
id uint32
|
||||
iteration uint32
|
||||
chainKey []byte
|
||||
version uint32
|
||||
signatureKey ecc.ECPublicKeyable
|
||||
serializer SenderKeyDistributionMessageSerializer
|
||||
}
|
||||
|
||||
// ID will return the message's id.
|
||||
func (p *SenderKeyDistributionMessage) ID() uint32 {
|
||||
return p.id
|
||||
}
|
||||
|
||||
// Iteration will return the message's iteration.
|
||||
func (p *SenderKeyDistributionMessage) Iteration() uint32 {
|
||||
return p.iteration
|
||||
}
|
||||
|
||||
// ChainKey will return the message's chain key in bytes.
|
||||
func (p *SenderKeyDistributionMessage) ChainKey() []byte {
|
||||
return p.chainKey
|
||||
}
|
||||
|
||||
// SignatureKey will return the message's signature public key
|
||||
func (p *SenderKeyDistributionMessage) SignatureKey() ecc.ECPublicKeyable {
|
||||
return p.signatureKey
|
||||
}
|
||||
|
||||
// Serialize will use the given serializer and return the message as
|
||||
// bytes.
|
||||
func (p *SenderKeyDistributionMessage) Serialize() []byte {
|
||||
structure := &SenderKeyDistributionMessageStructure{
|
||||
ID: p.id,
|
||||
Iteration: p.iteration,
|
||||
ChainKey: p.chainKey,
|
||||
SigningKey: p.signatureKey.Serialize(),
|
||||
Version: CurrentVersion,
|
||||
}
|
||||
return p.serializer.Serialize(structure)
|
||||
}
|
||||
|
||||
// Type will return the message's type.
|
||||
func (p *SenderKeyDistributionMessage) Type() uint32 {
|
||||
return SENDERKEY_DISTRIBUTION_TYPE
|
||||
}
|
||||
168
vendor/go.mau.fi/libsignal/protocol/SenderKeyMessage.go
vendored
Normal file
168
vendor/go.mau.fi/libsignal/protocol/SenderKeyMessage.go
vendored
Normal file
@@ -0,0 +1,168 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/signalerror"
|
||||
"go.mau.fi/libsignal/util/bytehelper"
|
||||
)
|
||||
|
||||
// SenderKeyMessageSerializer is an interface for serializing and deserializing
|
||||
// SenderKeyMessages into bytes. An implementation of this interface should be
|
||||
// used to encode/decode the object into JSON, Protobuffers, etc.
|
||||
type SenderKeyMessageSerializer interface {
|
||||
Serialize(signalMessage *SenderKeyMessageStructure) []byte
|
||||
Deserialize(serialized []byte) (*SenderKeyMessageStructure, error)
|
||||
}
|
||||
|
||||
// NewSenderKeyMessageFromBytes will return a Signal Ciphertext message from the given
|
||||
// bytes using the given serializer.
|
||||
func NewSenderKeyMessageFromBytes(serialized []byte,
|
||||
serializer SenderKeyMessageSerializer) (*SenderKeyMessage, error) {
|
||||
|
||||
// Use the given serializer to decode the signal message.
|
||||
senderKeyMessageStructure, err := serializer.Deserialize(serialized)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewSenderKeyMessageFromStruct(senderKeyMessageStructure, serializer)
|
||||
}
|
||||
|
||||
// NewSenderKeyMessageFromStruct returns a Signal Ciphertext message from the
|
||||
// given serializable structure.
|
||||
func NewSenderKeyMessageFromStruct(structure *SenderKeyMessageStructure,
|
||||
serializer SenderKeyMessageSerializer) (*SenderKeyMessage, error) {
|
||||
|
||||
// Throw an error if the given message structure is an unsupported version.
|
||||
if structure.Version <= UnsupportedVersion {
|
||||
return nil, fmt.Errorf("%w %d (sender key message)", signalerror.ErrOldMessageVersion, structure.Version)
|
||||
}
|
||||
|
||||
// Throw an error if the given message structure is a future version.
|
||||
if structure.Version > CurrentVersion {
|
||||
return nil, fmt.Errorf("%w %d (sender key message)", signalerror.ErrUnknownMessageVersion, structure.Version)
|
||||
}
|
||||
|
||||
// Throw an error if the structure is missing critical fields.
|
||||
if structure.CipherText == nil {
|
||||
return nil, fmt.Errorf("%w (sender key message)", signalerror.ErrIncompleteMessage)
|
||||
}
|
||||
|
||||
// Create the signal message object from the structure.
|
||||
whisperMessage := &SenderKeyMessage{
|
||||
keyID: structure.ID,
|
||||
version: structure.Version,
|
||||
iteration: structure.Iteration,
|
||||
ciphertext: structure.CipherText,
|
||||
signature: structure.Signature,
|
||||
serializer: serializer,
|
||||
}
|
||||
|
||||
return whisperMessage, nil
|
||||
}
|
||||
|
||||
// NewSenderKeyMessage returns a SenderKeyMessage.
|
||||
func NewSenderKeyMessage(keyID uint32, iteration uint32, ciphertext []byte,
|
||||
signatureKey ecc.ECPrivateKeyable, serializer SenderKeyMessageSerializer) *SenderKeyMessage {
|
||||
|
||||
// Ensure we have a valid signature key
|
||||
if signatureKey == nil {
|
||||
panic("Signature is nil. Unable to sign new senderkey message.")
|
||||
}
|
||||
|
||||
// Build our SenderKeyMessage.
|
||||
senderKeyMessage := &SenderKeyMessage{
|
||||
keyID: keyID,
|
||||
iteration: iteration,
|
||||
ciphertext: ciphertext,
|
||||
version: CurrentVersion,
|
||||
serializer: serializer,
|
||||
}
|
||||
|
||||
// Sign the serialized message and include it in the message. This will be included
|
||||
// in the signed serialized version of the message.
|
||||
signature := ecc.CalculateSignature(signatureKey, senderKeyMessage.Serialize())
|
||||
senderKeyMessage.signature = bytehelper.ArrayToSlice64(signature)
|
||||
|
||||
return senderKeyMessage
|
||||
}
|
||||
|
||||
// SenderKeyMessageStructure is a serializeable structure for SenderKey messages.
|
||||
type SenderKeyMessageStructure struct {
|
||||
ID uint32
|
||||
Iteration uint32
|
||||
CipherText []byte
|
||||
Version uint32
|
||||
Signature []byte
|
||||
}
|
||||
|
||||
// SenderKeyMessage is a structure for messages using senderkey groups.
|
||||
type SenderKeyMessage struct {
|
||||
version uint32
|
||||
keyID uint32
|
||||
iteration uint32
|
||||
ciphertext []byte
|
||||
signature []byte
|
||||
serializer SenderKeyMessageSerializer
|
||||
}
|
||||
|
||||
// KeyID returns the SenderKeyMessage key ID.
|
||||
func (p *SenderKeyMessage) KeyID() uint32 {
|
||||
return p.keyID
|
||||
}
|
||||
|
||||
// Iteration returns the SenderKeyMessage iteration.
|
||||
func (p *SenderKeyMessage) Iteration() uint32 {
|
||||
return p.iteration
|
||||
}
|
||||
|
||||
// Ciphertext returns the SenderKeyMessage encrypted ciphertext.
|
||||
func (p *SenderKeyMessage) Ciphertext() []byte {
|
||||
return p.ciphertext
|
||||
}
|
||||
|
||||
// Version returns the Signal message version of the message.
|
||||
func (p *SenderKeyMessage) Version() uint32 {
|
||||
return p.version
|
||||
}
|
||||
|
||||
// Serialize will use the given serializer to return the message as bytes
|
||||
// excluding the signature. This should be used for signing and verifying
|
||||
// message signatures.
|
||||
func (p *SenderKeyMessage) Serialize() []byte {
|
||||
structure := &SenderKeyMessageStructure{
|
||||
ID: p.keyID,
|
||||
Iteration: p.iteration,
|
||||
CipherText: p.ciphertext,
|
||||
Version: p.version,
|
||||
}
|
||||
|
||||
return p.serializer.Serialize(structure)
|
||||
}
|
||||
|
||||
// SignedSerialize will use the given serializer to return the message as
|
||||
// bytes with the message signature included. This should be used when
|
||||
// sending the message over the network.
|
||||
func (p *SenderKeyMessage) SignedSerialize() []byte {
|
||||
structure := &SenderKeyMessageStructure{
|
||||
ID: p.keyID,
|
||||
Iteration: p.iteration,
|
||||
CipherText: p.ciphertext,
|
||||
Version: p.version,
|
||||
Signature: p.signature,
|
||||
}
|
||||
|
||||
return p.serializer.Serialize(structure)
|
||||
}
|
||||
|
||||
// Signature returns the SenderKeyMessage signature
|
||||
func (p *SenderKeyMessage) Signature() [64]byte {
|
||||
return bytehelper.SliceToArray64(p.signature)
|
||||
}
|
||||
|
||||
// Type returns the sender key type.
|
||||
func (p *SenderKeyMessage) Type() uint32 {
|
||||
return SENDERKEY_TYPE
|
||||
}
|
||||
25
vendor/go.mau.fi/libsignal/protocol/SenderKeyName.go
vendored
Normal file
25
vendor/go.mau.fi/libsignal/protocol/SenderKeyName.go
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
package protocol
|
||||
|
||||
// NewSenderKeyName returns a new SenderKeyName object.
|
||||
func NewSenderKeyName(groupID string, sender *SignalAddress) *SenderKeyName {
|
||||
return &SenderKeyName{
|
||||
groupID: groupID,
|
||||
sender: sender,
|
||||
}
|
||||
}
|
||||
|
||||
// SenderKeyName is a structure for a group session address.
|
||||
type SenderKeyName struct {
|
||||
groupID string
|
||||
sender *SignalAddress
|
||||
}
|
||||
|
||||
// GroupID returns the sender key group id
|
||||
func (n *SenderKeyName) GroupID() string {
|
||||
return n.groupID
|
||||
}
|
||||
|
||||
// Sender returns the Signal address of sending user in the group.
|
||||
func (n *SenderKeyName) Sender() *SignalAddress {
|
||||
return n.sender
|
||||
}
|
||||
226
vendor/go.mau.fi/libsignal/protocol/SignalMessage.go
vendored
Normal file
226
vendor/go.mau.fi/libsignal/protocol/SignalMessage.go
vendored
Normal file
@@ -0,0 +1,226 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/keys/identity"
|
||||
"go.mau.fi/libsignal/logger"
|
||||
"go.mau.fi/libsignal/signalerror"
|
||||
"go.mau.fi/libsignal/util/bytehelper"
|
||||
)
|
||||
|
||||
const MacLength int = 8
|
||||
|
||||
// SignalMessageSerializer is an interface for serializing and deserializing
|
||||
// SignalMessages into bytes. An implementation of this interface should be
|
||||
// used to encode/decode the object into JSON, Protobuffers, etc.
|
||||
type SignalMessageSerializer interface {
|
||||
Serialize(signalMessage *SignalMessageStructure) []byte
|
||||
Deserialize(serialized []byte) (*SignalMessageStructure, error)
|
||||
}
|
||||
|
||||
// NewSignalMessageFromBytes will return a Signal Ciphertext message from the given
|
||||
// bytes using the given serializer.
|
||||
func NewSignalMessageFromBytes(serialized []byte, serializer SignalMessageSerializer) (*SignalMessage, error) {
|
||||
// Use the given serializer to decode the signal message.
|
||||
signalMessageStructure, err := serializer.Deserialize(serialized)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewSignalMessageFromStruct(signalMessageStructure, serializer)
|
||||
}
|
||||
|
||||
// NewSignalMessageFromStruct returns a Signal Ciphertext message from the
|
||||
// given serializable structure.
|
||||
func NewSignalMessageFromStruct(structure *SignalMessageStructure, serializer SignalMessageSerializer) (*SignalMessage, error) {
|
||||
// Throw an error if the given message structure is an unsupported version.
|
||||
if structure.Version <= UnsupportedVersion {
|
||||
return nil, fmt.Errorf("%w %d (normal message)", signalerror.ErrOldMessageVersion, structure.Version)
|
||||
}
|
||||
|
||||
// Throw an error if the given message structure is a future version.
|
||||
if structure.Version > CurrentVersion {
|
||||
return nil, fmt.Errorf("%w %d (normal message)", signalerror.ErrUnknownMessageVersion, structure.Version)
|
||||
}
|
||||
|
||||
// Throw an error if the structure is missing critical fields.
|
||||
if structure.CipherText == nil || structure.RatchetKey == nil {
|
||||
return nil, fmt.Errorf("%w (normal message)", signalerror.ErrIncompleteMessage)
|
||||
}
|
||||
|
||||
// Create the signal message object from the structure.
|
||||
whisperMessage := &SignalMessage{structure: *structure, serializer: serializer}
|
||||
|
||||
// Generate the ECC key from bytes.
|
||||
var err error
|
||||
whisperMessage.senderRatchetKey, err = ecc.DecodePoint(structure.RatchetKey, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return whisperMessage, nil
|
||||
}
|
||||
|
||||
// NewSignalMessage returns a Signal Ciphertext message.
|
||||
func NewSignalMessage(messageVersion int, counter, previousCounter uint32, macKey []byte,
|
||||
senderRatchetKey ecc.ECPublicKeyable, ciphertext []byte, senderIdentityKey,
|
||||
receiverIdentityKey *identity.Key, serializer SignalMessageSerializer) (*SignalMessage, error) {
|
||||
|
||||
version := []byte(strconv.Itoa(messageVersion))
|
||||
// Build the signal message structure with the given data.
|
||||
structure := &SignalMessageStructure{
|
||||
Counter: counter,
|
||||
PreviousCounter: previousCounter,
|
||||
RatchetKey: senderRatchetKey.Serialize(),
|
||||
CipherText: ciphertext,
|
||||
}
|
||||
|
||||
serialized := append(version, serializer.Serialize(structure)...)
|
||||
// Get the message authentication code from the serialized structure.
|
||||
mac, err := getMac(
|
||||
messageVersion, senderIdentityKey, receiverIdentityKey,
|
||||
macKey, serialized,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
structure.Mac = mac
|
||||
structure.Version = messageVersion
|
||||
|
||||
// Generate a SignalMessage with the structure.
|
||||
whisperMessage, err := NewSignalMessageFromStruct(structure, serializer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return whisperMessage, nil
|
||||
}
|
||||
|
||||
// SignalMessageStructure is a serializeable structure of a signal message
|
||||
// object.
|
||||
type SignalMessageStructure struct {
|
||||
RatchetKey []byte
|
||||
Counter uint32
|
||||
PreviousCounter uint32
|
||||
CipherText []byte
|
||||
Version int
|
||||
Mac []byte
|
||||
}
|
||||
|
||||
// SignalMessage is a cipher message that contains a message encrypted
|
||||
// with the Signal protocol.
|
||||
type SignalMessage struct {
|
||||
structure SignalMessageStructure
|
||||
senderRatchetKey ecc.ECPublicKeyable
|
||||
serializer SignalMessageSerializer
|
||||
}
|
||||
|
||||
// SenderRatchetKey returns the SignalMessage's sender ratchet key. This
|
||||
// key is used for ratcheting the chain forward to negotiate a new shared
|
||||
// secret that cannot be derived from previous chains.
|
||||
func (s *SignalMessage) SenderRatchetKey() ecc.ECPublicKeyable {
|
||||
return s.senderRatchetKey
|
||||
}
|
||||
|
||||
// MessageVersion returns the message version this SignalMessage supports.
|
||||
func (s *SignalMessage) MessageVersion() int {
|
||||
return s.structure.Version
|
||||
}
|
||||
|
||||
// Counter will return the SignalMessage counter.
|
||||
func (s *SignalMessage) Counter() uint32 {
|
||||
return s.structure.Counter
|
||||
}
|
||||
|
||||
// Body will return the SignalMessage's ciphertext in bytes.
|
||||
func (s *SignalMessage) Body() []byte {
|
||||
return s.structure.CipherText
|
||||
}
|
||||
|
||||
// VerifyMac will return an error if the message's message authentication code
|
||||
// is invalid. This should be used on SignalMessages that have been constructed
|
||||
// from a sent message.
|
||||
func (s *SignalMessage) VerifyMac(messageVersion int, senderIdentityKey,
|
||||
receiverIdentityKey *identity.Key, macKey []byte) error {
|
||||
|
||||
// Create a copy of the message without the mac. We'll use this to calculate
|
||||
// the message authentication code.
|
||||
structure := s.structure
|
||||
signalMessage, err := NewSignalMessageFromStruct(&structure, s.serializer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
signalMessage.structure.Mac = nil
|
||||
signalMessage.structure.Version = 0
|
||||
version := []byte(strconv.Itoa(s.MessageVersion()))
|
||||
serialized := append(version, signalMessage.Serialize()...)
|
||||
|
||||
// Calculate the message authentication code from the serialized structure.
|
||||
ourMac, err := getMac(
|
||||
messageVersion,
|
||||
senderIdentityKey,
|
||||
receiverIdentityKey,
|
||||
macKey,
|
||||
serialized,
|
||||
)
|
||||
if err != nil {
|
||||
logger.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the message authentication code that was sent to us as part of
|
||||
// the signal message structure.
|
||||
theirMac := s.structure.Mac
|
||||
|
||||
logger.Debug("Verifying macs...")
|
||||
logger.Debug(" Our MAC: ", ourMac)
|
||||
logger.Debug(" Their MAC: ", theirMac)
|
||||
|
||||
// Return an error if our calculated mac doesn't match the mac sent to us.
|
||||
if !hmac.Equal(ourMac, theirMac) {
|
||||
return signalerror.ErrBadMAC
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Serialize will return the Signal Message as bytes.
|
||||
func (s *SignalMessage) Serialize() []byte {
|
||||
return s.serializer.Serialize(&s.structure)
|
||||
}
|
||||
|
||||
// Structure will return a serializeable structure of the Signal Message.
|
||||
func (s *SignalMessage) Structure() *SignalMessageStructure {
|
||||
structure := s.structure
|
||||
return &structure
|
||||
}
|
||||
|
||||
// Type will return the type of Signal Message this is.
|
||||
func (s *SignalMessage) Type() uint32 {
|
||||
return WHISPER_TYPE
|
||||
}
|
||||
|
||||
// getMac will calculate the mac using the given message version, identity
|
||||
// keys, macKey and SignalMessageStructure. The MAC key is a private key held
|
||||
// by both parties that is concatenated with the message and hashed.
|
||||
func getMac(messageVersion int, senderIdentityKey, receiverIdentityKey *identity.Key,
|
||||
macKey, serialized []byte) ([]byte, error) {
|
||||
|
||||
mac := hmac.New(sha256.New, macKey[:])
|
||||
|
||||
if messageVersion >= 3 {
|
||||
mac.Write(senderIdentityKey.PublicKey().Serialize())
|
||||
mac.Write(receiverIdentityKey.PublicKey().Serialize())
|
||||
}
|
||||
|
||||
mac.Write(serialized)
|
||||
|
||||
fullMac := mac.Sum(nil)
|
||||
|
||||
return bytehelper.Trim(fullMac, MacLength), nil
|
||||
}
|
||||
38
vendor/go.mau.fi/libsignal/protocol/SignalProtocolAddress.go
vendored
Normal file
38
vendor/go.mau.fi/libsignal/protocol/SignalProtocolAddress.go
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
const ADDRESS_SEPARATOR = ":"
|
||||
|
||||
// NewSignalAddress returns a new signal address.
|
||||
func NewSignalAddress(name string, deviceID uint32) *SignalAddress {
|
||||
addr := SignalAddress{
|
||||
name: name,
|
||||
deviceID: deviceID,
|
||||
}
|
||||
|
||||
return &addr
|
||||
}
|
||||
|
||||
// SignalAddress is a combination of a name and a device ID.
|
||||
type SignalAddress struct {
|
||||
name string
|
||||
deviceID uint32
|
||||
}
|
||||
|
||||
// Name returns the signal address's name.
|
||||
func (s *SignalAddress) Name() string {
|
||||
return s.name
|
||||
}
|
||||
|
||||
// DeviceID returns the signal address's device ID.
|
||||
func (s *SignalAddress) DeviceID() uint32 {
|
||||
return s.deviceID
|
||||
}
|
||||
|
||||
// String returns a string of both the address name and device id.
|
||||
func (s *SignalAddress) String() string {
|
||||
return fmt.Sprintf("%s%s%d", s.name, ADDRESS_SEPARATOR, s.deviceID)
|
||||
}
|
||||
197
vendor/go.mau.fi/libsignal/ratchet/Ratchet.go
vendored
Normal file
197
vendor/go.mau.fi/libsignal/ratchet/Ratchet.go
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
// Package ratchet provides the methods necessary to establish a new double
|
||||
// ratchet session.
|
||||
package ratchet
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/kdf"
|
||||
"go.mau.fi/libsignal/keys/chain"
|
||||
"go.mau.fi/libsignal/keys/root"
|
||||
"go.mau.fi/libsignal/keys/session"
|
||||
)
|
||||
|
||||
var b64 = base64.StdEncoding.EncodeToString
|
||||
|
||||
func genDiscontinuity() [32]byte {
|
||||
var discontinuity [32]byte
|
||||
for i := range discontinuity {
|
||||
discontinuity[i] = 0xFF
|
||||
}
|
||||
return discontinuity
|
||||
}
|
||||
|
||||
// CalculateSenderSession calculates the key agreement for a recipient. This
|
||||
// should be used when we are trying to send a message to someone for the
|
||||
// first time.
|
||||
func CalculateSenderSession(parameters *SenderParameters) (*session.KeyPair, error) {
|
||||
var secret [32]byte
|
||||
var publicKey [32]byte
|
||||
var privateKey [32]byte
|
||||
masterSecret := []byte{} // Create a master shared secret that is 5 different 32-byte values
|
||||
discontinuity := genDiscontinuity()
|
||||
masterSecret = append(masterSecret, discontinuity[:]...)
|
||||
|
||||
// Calculate the agreement using their signed prekey and our identity key.
|
||||
publicKey = parameters.TheirSignedPreKey().PublicKey()
|
||||
privateKey = parameters.OurIdentityKey().PrivateKey().Serialize()
|
||||
secret = kdf.CalculateSharedSecret(
|
||||
publicKey,
|
||||
privateKey,
|
||||
)
|
||||
masterSecret = append(masterSecret, secret[:]...)
|
||||
|
||||
// Calculate the agreement using their identity key and our base key.
|
||||
publicKey = parameters.TheirIdentityKey().PublicKey().PublicKey()
|
||||
privateKey = parameters.OurBaseKey().PrivateKey().Serialize()
|
||||
secret = kdf.CalculateSharedSecret(
|
||||
publicKey,
|
||||
privateKey,
|
||||
)
|
||||
masterSecret = append(masterSecret, secret[:]...)
|
||||
|
||||
// Calculate the agreement using their signed prekey and our base key.
|
||||
publicKey = parameters.TheirSignedPreKey().PublicKey()
|
||||
privateKey = parameters.OurBaseKey().PrivateKey().Serialize()
|
||||
secret = kdf.CalculateSharedSecret(
|
||||
publicKey,
|
||||
privateKey,
|
||||
)
|
||||
masterSecret = append(masterSecret, secret[:]...)
|
||||
|
||||
// If they have a one-time prekey, use it to calculate the shared secret with their
|
||||
// one time key and our base key.
|
||||
if parameters.TheirOneTimePreKey() != nil {
|
||||
publicKey = parameters.TheirOneTimePreKey().PublicKey()
|
||||
privateKey = parameters.OurBaseKey().PrivateKey().Serialize()
|
||||
secret = kdf.CalculateSharedSecret(
|
||||
publicKey,
|
||||
privateKey,
|
||||
)
|
||||
masterSecret = append(masterSecret, secret[:]...)
|
||||
|
||||
}
|
||||
|
||||
// Derive the root and chain keys based on the master secret.
|
||||
derivedKeysBytes, err := kdf.DeriveSecrets(masterSecret, nil, []byte("WhisperText"), root.DerivedSecretsSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
derivedKeys := session.NewDerivedSecrets(derivedKeysBytes)
|
||||
chainKey := chain.NewKey(kdf.DeriveSecrets, derivedKeys.ChainKey(), 0)
|
||||
rootKey := root.NewKey(kdf.DeriveSecrets, derivedKeys.RootKey())
|
||||
|
||||
// Add the root and chain keys to a structure that will hold both keys.
|
||||
sessionKeys := session.NewKeyPair(rootKey, chainKey)
|
||||
|
||||
return sessionKeys, nil
|
||||
}
|
||||
|
||||
// CalculateReceiverSession calculates the key agreement for a sender. This should
|
||||
// be used when we are receiving a message from someone for the first time.
|
||||
func CalculateReceiverSession(parameters *ReceiverParameters) (*session.KeyPair, error) {
|
||||
var secret [32]byte
|
||||
var publicKey [32]byte
|
||||
var privateKey [32]byte
|
||||
masterSecret := []byte{} // Create a master shared secret that is 5 different 32-byte values
|
||||
|
||||
discontinuity := genDiscontinuity()
|
||||
masterSecret = append(masterSecret, discontinuity[:]...)
|
||||
|
||||
// Calculate the agreement using their identity key and our signed pre key.
|
||||
publicKey = parameters.TheirIdentityKey().PublicKey().PublicKey()
|
||||
privateKey = parameters.OurSignedPreKey().PrivateKey().Serialize()
|
||||
secret = kdf.CalculateSharedSecret(
|
||||
publicKey,
|
||||
privateKey,
|
||||
)
|
||||
masterSecret = append(masterSecret, secret[:]...)
|
||||
|
||||
// Calculate the agreement using their base key and our identity key.
|
||||
publicKey = parameters.TheirBaseKey().PublicKey()
|
||||
privateKey = parameters.OurIdentityKeyPair().PrivateKey().Serialize()
|
||||
secret = kdf.CalculateSharedSecret(
|
||||
publicKey,
|
||||
privateKey,
|
||||
)
|
||||
masterSecret = append(masterSecret, secret[:]...)
|
||||
|
||||
// Calculate the agreement using their base key and our signed prekey.
|
||||
publicKey = parameters.TheirBaseKey().PublicKey()
|
||||
privateKey = parameters.OurSignedPreKey().PrivateKey().Serialize()
|
||||
secret = kdf.CalculateSharedSecret(
|
||||
publicKey,
|
||||
privateKey,
|
||||
)
|
||||
masterSecret = append(masterSecret, secret[:]...)
|
||||
|
||||
// If we had a one-time prekey, use it to calculate the shared secret with our
|
||||
// one time key and their base key.
|
||||
if parameters.OurOneTimePreKey() != nil {
|
||||
publicKey = parameters.TheirBaseKey().PublicKey()
|
||||
privateKey = parameters.OurOneTimePreKey().PrivateKey().Serialize()
|
||||
secret = kdf.CalculateSharedSecret(
|
||||
publicKey,
|
||||
privateKey,
|
||||
)
|
||||
masterSecret = append(masterSecret, secret[:]...)
|
||||
|
||||
}
|
||||
|
||||
// Derive the root and chain keys based on the master secret.
|
||||
derivedKeysBytes, err := kdf.DeriveSecrets(masterSecret, nil, []byte("WhisperText"), root.DerivedSecretsSize)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
derivedKeys := session.NewDerivedSecrets(derivedKeysBytes)
|
||||
chainKey := chain.NewKey(kdf.DeriveSecrets, derivedKeys.ChainKey(), 0)
|
||||
rootKey := root.NewKey(kdf.DeriveSecrets, derivedKeys.RootKey())
|
||||
|
||||
// Add the root and chain keys to a structure that will hold both keys.
|
||||
sessionKeys := session.NewKeyPair(rootKey, chainKey)
|
||||
|
||||
return sessionKeys, nil
|
||||
}
|
||||
|
||||
// CalculateSymmetricSession calculates the key agreement between two users. This
|
||||
// works by both clients exchanging KeyExchange messages to first establish a session.
|
||||
// This is useful for establishing a session if both users are online.
|
||||
func CalculateSymmetricSession(parameters *SymmetricParameters) (*session.KeyPair, error) {
|
||||
// Compare the base public keys so we can deterministically know whether we should
|
||||
// be setting up a sender or receiver session. If our key converted to an integer is
|
||||
// less than the other user's, act as a sender.
|
||||
if isSender(parameters.OurBaseKey.PublicKey(), parameters.TheirBaseKey) {
|
||||
senderParameters := &SenderParameters{
|
||||
ourBaseKey: parameters.OurBaseKey,
|
||||
ourIdentityKeyPair: parameters.OurIdentityKeyPair,
|
||||
theirRatchetKey: parameters.TheirRatchetKey,
|
||||
theirIdentityKey: parameters.TheirIdentityKey,
|
||||
theirSignedPreKey: parameters.TheirBaseKey,
|
||||
}
|
||||
|
||||
return CalculateSenderSession(senderParameters)
|
||||
}
|
||||
|
||||
// If our base public key was larger than the other user's, act as a receiver.
|
||||
receiverParameters := &ReceiverParameters{
|
||||
ourIdentityKeyPair: parameters.OurIdentityKeyPair,
|
||||
ourRatchetKey: parameters.OurRatchetKey,
|
||||
ourSignedPreKey: parameters.OurBaseKey,
|
||||
theirBaseKey: parameters.TheirBaseKey,
|
||||
theirIdentityKey: parameters.TheirIdentityKey,
|
||||
}
|
||||
|
||||
return CalculateReceiverSession(receiverParameters)
|
||||
}
|
||||
|
||||
// isSender is a private method for determining if a symmetric session should
|
||||
// be calculated as the sender or receiver. It does so by converting the given
|
||||
// keys into integers and comparing the size of those integers.
|
||||
func isSender(ourKey, theirKey ecc.ECPublicKeyable) bool {
|
||||
ourKeyInt := binary.BigEndian.Uint32(ourKey.Serialize())
|
||||
theirKeyInt := binary.BigEndian.Uint32(theirKey.Serialize())
|
||||
|
||||
return ourKeyInt < theirKeyInt
|
||||
}
|
||||
106
vendor/go.mau.fi/libsignal/ratchet/ReceiverParameters.go
vendored
Normal file
106
vendor/go.mau.fi/libsignal/ratchet/ReceiverParameters.go
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
package ratchet
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/keys/identity"
|
||||
)
|
||||
|
||||
// NewReceiverParameters creates a structure with all the keys needed to construct
|
||||
// a new session when we are receiving a message from a user for the first time.
|
||||
func NewReceiverParameters(ourIdentityKey *identity.KeyPair, ourSignedPreKey *ecc.ECKeyPair,
|
||||
ourOneTimePreKey *ecc.ECKeyPair, ourRatchetKey *ecc.ECKeyPair,
|
||||
theirBaseKey ecc.ECPublicKeyable, theirIdentityKey *identity.Key) *ReceiverParameters {
|
||||
|
||||
receiverParams := ReceiverParameters{
|
||||
ourIdentityKeyPair: ourIdentityKey,
|
||||
ourSignedPreKey: ourSignedPreKey,
|
||||
ourOneTimePreKey: ourOneTimePreKey,
|
||||
ourRatchetKey: ourRatchetKey,
|
||||
theirBaseKey: theirBaseKey,
|
||||
theirIdentityKey: theirIdentityKey,
|
||||
}
|
||||
|
||||
return &receiverParams
|
||||
}
|
||||
|
||||
// NewEmptyReceiverParameters creates an empty structure with the receiver parameters
|
||||
// needed to create a session. You should use the `set` functions to set all the
|
||||
// necessary keys needed to build a session.
|
||||
func NewEmptyReceiverParameters() *ReceiverParameters {
|
||||
receiverParams := ReceiverParameters{}
|
||||
|
||||
return &receiverParams
|
||||
}
|
||||
|
||||
// ReceiverParameters describes the session parameters if we are receiving
|
||||
// a message from someone for the first time. These parameters are used as
|
||||
// the basis for deriving a shared secret with the sender.
|
||||
type ReceiverParameters struct {
|
||||
ourIdentityKeyPair *identity.KeyPair
|
||||
ourSignedPreKey *ecc.ECKeyPair
|
||||
ourOneTimePreKey *ecc.ECKeyPair
|
||||
ourRatchetKey *ecc.ECKeyPair
|
||||
|
||||
theirBaseKey ecc.ECPublicKeyable
|
||||
theirIdentityKey *identity.Key
|
||||
}
|
||||
|
||||
// OurIdentityKeyPair returns the identity key of the receiver.
|
||||
func (r *ReceiverParameters) OurIdentityKeyPair() *identity.KeyPair {
|
||||
return r.ourIdentityKeyPair
|
||||
}
|
||||
|
||||
// OurSignedPreKey returns the signed prekey of the receiver.
|
||||
func (r *ReceiverParameters) OurSignedPreKey() *ecc.ECKeyPair {
|
||||
return r.ourSignedPreKey
|
||||
}
|
||||
|
||||
// OurOneTimePreKey returns the one time prekey of the receiver.
|
||||
func (r *ReceiverParameters) OurOneTimePreKey() *ecc.ECKeyPair {
|
||||
return r.ourOneTimePreKey
|
||||
}
|
||||
|
||||
// OurRatchetKey returns the ratchet key of the receiver.
|
||||
func (r *ReceiverParameters) OurRatchetKey() *ecc.ECKeyPair {
|
||||
return r.ourRatchetKey
|
||||
}
|
||||
|
||||
// TheirBaseKey returns the base key of the sender.
|
||||
func (r *ReceiverParameters) TheirBaseKey() ecc.ECPublicKeyable {
|
||||
return r.theirBaseKey
|
||||
}
|
||||
|
||||
// TheirIdentityKey returns the identity key of the sender.
|
||||
func (r *ReceiverParameters) TheirIdentityKey() *identity.Key {
|
||||
return r.theirIdentityKey
|
||||
}
|
||||
|
||||
// SetOurIdentityKeyPair sets the identity key of the receiver.
|
||||
func (r *ReceiverParameters) SetOurIdentityKeyPair(ourIdentityKey *identity.KeyPair) {
|
||||
r.ourIdentityKeyPair = ourIdentityKey
|
||||
}
|
||||
|
||||
// SetOurSignedPreKey sets the signed prekey of the receiver.
|
||||
func (r *ReceiverParameters) SetOurSignedPreKey(ourSignedPreKey *ecc.ECKeyPair) {
|
||||
r.ourSignedPreKey = ourSignedPreKey
|
||||
}
|
||||
|
||||
// SetOurOneTimePreKey sets the one time prekey of the receiver.
|
||||
func (r *ReceiverParameters) SetOurOneTimePreKey(ourOneTimePreKey *ecc.ECKeyPair) {
|
||||
r.ourOneTimePreKey = ourOneTimePreKey
|
||||
}
|
||||
|
||||
// SetOurRatchetKey sets the ratchet key of the receiver.
|
||||
func (r *ReceiverParameters) SetOurRatchetKey(ourRatchetKey *ecc.ECKeyPair) {
|
||||
r.ourRatchetKey = ourRatchetKey
|
||||
}
|
||||
|
||||
// SetTheirBaseKey sets the base key of the sender.
|
||||
func (r *ReceiverParameters) SetTheirBaseKey(theirBaseKey ecc.ECPublicKeyable) {
|
||||
r.theirBaseKey = theirBaseKey
|
||||
}
|
||||
|
||||
// SetTheirIdentityKey sets the identity key of the sender.
|
||||
func (r *ReceiverParameters) SetTheirIdentityKey(theirIdentityKey *identity.Key) {
|
||||
r.theirIdentityKey = theirIdentityKey
|
||||
}
|
||||
106
vendor/go.mau.fi/libsignal/ratchet/SenderParameters.go
vendored
Normal file
106
vendor/go.mau.fi/libsignal/ratchet/SenderParameters.go
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
package ratchet
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/keys/identity"
|
||||
)
|
||||
|
||||
// NewSenderParameters creates a structure with all the keys needed to construct
|
||||
// a new session when we are sending a message to a recipient for the first time.
|
||||
func NewSenderParameters(ourIdentityKey *identity.KeyPair, ourBaseKey *ecc.ECKeyPair,
|
||||
theirIdentityKey *identity.Key, theirSignedPreKey ecc.ECPublicKeyable,
|
||||
theirRatchetKey ecc.ECPublicKeyable, theirOneTimePreKey ecc.ECPublicKeyable) *SenderParameters {
|
||||
|
||||
senderParams := SenderParameters{
|
||||
ourIdentityKeyPair: ourIdentityKey,
|
||||
ourBaseKey: ourBaseKey,
|
||||
theirIdentityKey: theirIdentityKey,
|
||||
theirSignedPreKey: theirSignedPreKey,
|
||||
theirOneTimePreKey: theirOneTimePreKey,
|
||||
theirRatchetKey: theirRatchetKey,
|
||||
}
|
||||
|
||||
return &senderParams
|
||||
}
|
||||
|
||||
// NewEmptySenderParameters creates an empty structure with the sender parameters
|
||||
// needed to create a session. You should use the `set` functions to set all the
|
||||
// necessary keys needed to build a session.
|
||||
func NewEmptySenderParameters() *SenderParameters {
|
||||
senderParams := SenderParameters{}
|
||||
|
||||
return &senderParams
|
||||
}
|
||||
|
||||
// SenderParameters describes the session parameters if we are sending the
|
||||
// recipient a message for the first time. These parameters are used as the
|
||||
// basis for deriving a shared secret with a recipient.
|
||||
type SenderParameters struct {
|
||||
ourIdentityKeyPair *identity.KeyPair
|
||||
ourBaseKey *ecc.ECKeyPair
|
||||
|
||||
theirIdentityKey *identity.Key
|
||||
theirSignedPreKey ecc.ECPublicKeyable
|
||||
theirOneTimePreKey ecc.ECPublicKeyable
|
||||
theirRatchetKey ecc.ECPublicKeyable
|
||||
}
|
||||
|
||||
// OurIdentityKey returns the identity key pair of the sender.
|
||||
func (s *SenderParameters) OurIdentityKey() *identity.KeyPair {
|
||||
return s.ourIdentityKeyPair
|
||||
}
|
||||
|
||||
// OurBaseKey returns the base ECC key pair of the sender.
|
||||
func (s *SenderParameters) OurBaseKey() *ecc.ECKeyPair {
|
||||
return s.ourBaseKey
|
||||
}
|
||||
|
||||
// TheirIdentityKey returns the identity public key of the receiver.
|
||||
func (s *SenderParameters) TheirIdentityKey() *identity.Key {
|
||||
return s.theirIdentityKey
|
||||
}
|
||||
|
||||
// TheirSignedPreKey returns the signed pre key of the receiver.
|
||||
func (s *SenderParameters) TheirSignedPreKey() ecc.ECPublicKeyable {
|
||||
return s.theirSignedPreKey
|
||||
}
|
||||
|
||||
// TheirOneTimePreKey returns the receiver's one time prekey.
|
||||
func (s *SenderParameters) TheirOneTimePreKey() ecc.ECPublicKeyable {
|
||||
return s.theirOneTimePreKey
|
||||
}
|
||||
|
||||
// TheirRatchetKey returns the receiver's ratchet key.
|
||||
func (s *SenderParameters) TheirRatchetKey() ecc.ECPublicKeyable {
|
||||
return s.theirRatchetKey
|
||||
}
|
||||
|
||||
// SetOurIdentityKey sets the identity key pair of the sender.
|
||||
func (s *SenderParameters) SetOurIdentityKey(ourIdentityKey *identity.KeyPair) {
|
||||
s.ourIdentityKeyPair = ourIdentityKey
|
||||
}
|
||||
|
||||
// SetOurBaseKey sets the base ECC key pair of the sender.
|
||||
func (s *SenderParameters) SetOurBaseKey(ourBaseKey *ecc.ECKeyPair) {
|
||||
s.ourBaseKey = ourBaseKey
|
||||
}
|
||||
|
||||
// SetTheirIdentityKey sets the identity public key of the receiver.
|
||||
func (s *SenderParameters) SetTheirIdentityKey(theirIdentityKey *identity.Key) {
|
||||
s.theirIdentityKey = theirIdentityKey
|
||||
}
|
||||
|
||||
// SetTheirSignedPreKey sets the signed pre key of the receiver.
|
||||
func (s *SenderParameters) SetTheirSignedPreKey(theirSignedPreKey ecc.ECPublicKeyable) {
|
||||
s.theirSignedPreKey = theirSignedPreKey
|
||||
}
|
||||
|
||||
// SetTheirOneTimePreKey sets the receiver's one time prekey.
|
||||
func (s *SenderParameters) SetTheirOneTimePreKey(theirOneTimePreKey ecc.ECPublicKeyable) {
|
||||
s.theirOneTimePreKey = theirOneTimePreKey
|
||||
}
|
||||
|
||||
// SetTheirRatchetKey sets the receiver's ratchet key.
|
||||
func (s *SenderParameters) SetTheirRatchetKey(theirRatchetKey ecc.ECPublicKeyable) {
|
||||
s.theirRatchetKey = theirRatchetKey
|
||||
}
|
||||
18
vendor/go.mau.fi/libsignal/ratchet/SymmetricParameters.go
vendored
Normal file
18
vendor/go.mau.fi/libsignal/ratchet/SymmetricParameters.go
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
package ratchet
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/keys/identity"
|
||||
)
|
||||
|
||||
// SymmetricParameters describes the session parameters for sessions where
|
||||
// both users are online, which doesn't use prekeys for setup.
|
||||
type SymmetricParameters struct {
|
||||
OurBaseKey *ecc.ECKeyPair
|
||||
OurRatchetKey *ecc.ECKeyPair
|
||||
OurIdentityKeyPair *identity.KeyPair
|
||||
|
||||
TheirBaseKey ecc.ECPublicKeyable
|
||||
TheirRatchetKey ecc.ECPublicKeyable
|
||||
TheirIdentityKey *identity.Key
|
||||
}
|
||||
245
vendor/go.mau.fi/libsignal/serialize/FingerprintProtocol.pb.go
vendored
Normal file
245
vendor/go.mau.fi/libsignal/serialize/FingerprintProtocol.pb.go
vendored
Normal file
@@ -0,0 +1,245 @@
|
||||
// From https://github.com/signalapp/libsignal-protocol-c/blob/master/protobuf/FingerprintProtocol.proto
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc v3.12.4
|
||||
// source: serialize/FingerprintProtocol.proto
|
||||
|
||||
package serialize
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type LogicalFingerprint struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Content []byte `protobuf:"bytes,1,opt,name=content" json:"content,omitempty"`
|
||||
Identifier []byte `protobuf:"bytes,2,opt,name=identifier" json:"identifier,omitempty"` // Version 0
|
||||
}
|
||||
|
||||
func (x *LogicalFingerprint) Reset() {
|
||||
*x = LogicalFingerprint{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_serialize_FingerprintProtocol_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *LogicalFingerprint) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*LogicalFingerprint) ProtoMessage() {}
|
||||
|
||||
func (x *LogicalFingerprint) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_serialize_FingerprintProtocol_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use LogicalFingerprint.ProtoReflect.Descriptor instead.
|
||||
func (*LogicalFingerprint) Descriptor() ([]byte, []int) {
|
||||
return file_serialize_FingerprintProtocol_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *LogicalFingerprint) GetContent() []byte {
|
||||
if x != nil {
|
||||
return x.Content
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *LogicalFingerprint) GetIdentifier() []byte {
|
||||
if x != nil {
|
||||
return x.Identifier
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type CombinedFingerprints struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Version *uint32 `protobuf:"varint,1,opt,name=version" json:"version,omitempty"`
|
||||
LocalFingerprint *LogicalFingerprint `protobuf:"bytes,2,opt,name=localFingerprint" json:"localFingerprint,omitempty"`
|
||||
RemoteFingerprint *LogicalFingerprint `protobuf:"bytes,3,opt,name=remoteFingerprint" json:"remoteFingerprint,omitempty"`
|
||||
}
|
||||
|
||||
func (x *CombinedFingerprints) Reset() {
|
||||
*x = CombinedFingerprints{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_serialize_FingerprintProtocol_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *CombinedFingerprints) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*CombinedFingerprints) ProtoMessage() {}
|
||||
|
||||
func (x *CombinedFingerprints) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_serialize_FingerprintProtocol_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use CombinedFingerprints.ProtoReflect.Descriptor instead.
|
||||
func (*CombinedFingerprints) Descriptor() ([]byte, []int) {
|
||||
return file_serialize_FingerprintProtocol_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *CombinedFingerprints) GetVersion() uint32 {
|
||||
if x != nil && x.Version != nil {
|
||||
return *x.Version
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *CombinedFingerprints) GetLocalFingerprint() *LogicalFingerprint {
|
||||
if x != nil {
|
||||
return x.LocalFingerprint
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *CombinedFingerprints) GetRemoteFingerprint() *LogicalFingerprint {
|
||||
if x != nil {
|
||||
return x.RemoteFingerprint
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_serialize_FingerprintProtocol_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_serialize_FingerprintProtocol_proto_rawDesc = []byte{
|
||||
0x0a, 0x23, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x2f, 0x46, 0x69, 0x6e, 0x67,
|
||||
0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x74, 0x65, 0x78, 0x74, 0x73, 0x65, 0x63, 0x75, 0x72,
|
||||
0x65, 0x22, 0x4e, 0x0a, 0x12, 0x4c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x46, 0x69, 0x6e, 0x67,
|
||||
0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65,
|
||||
0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e,
|
||||
0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65,
|
||||
0x72, 0x22, 0xca, 0x01, 0x0a, 0x14, 0x43, 0x6f, 0x6d, 0x62, 0x69, 0x6e, 0x65, 0x64, 0x46, 0x69,
|
||||
0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65,
|
||||
0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x76, 0x65, 0x72,
|
||||
0x73, 0x69, 0x6f, 0x6e, 0x12, 0x4a, 0x0a, 0x10, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x46, 0x69, 0x6e,
|
||||
0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e,
|
||||
0x2e, 0x74, 0x65, 0x78, 0x74, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x69,
|
||||
0x63, 0x61, 0x6c, 0x46, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x52, 0x10,
|
||||
0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x46, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74,
|
||||
0x12, 0x4c, 0x0a, 0x11, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x46, 0x69, 0x6e, 0x67, 0x65, 0x72,
|
||||
0x70, 0x72, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x74, 0x65,
|
||||
0x78, 0x74, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c,
|
||||
0x46, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x52, 0x11, 0x72, 0x65, 0x6d,
|
||||
0x6f, 0x74, 0x65, 0x46, 0x69, 0x6e, 0x67, 0x65, 0x72, 0x70, 0x72, 0x69, 0x6e, 0x74,
|
||||
}
|
||||
|
||||
var (
|
||||
file_serialize_FingerprintProtocol_proto_rawDescOnce sync.Once
|
||||
file_serialize_FingerprintProtocol_proto_rawDescData = file_serialize_FingerprintProtocol_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_serialize_FingerprintProtocol_proto_rawDescGZIP() []byte {
|
||||
file_serialize_FingerprintProtocol_proto_rawDescOnce.Do(func() {
|
||||
file_serialize_FingerprintProtocol_proto_rawDescData = protoimpl.X.CompressGZIP(file_serialize_FingerprintProtocol_proto_rawDescData)
|
||||
})
|
||||
return file_serialize_FingerprintProtocol_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_serialize_FingerprintProtocol_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_serialize_FingerprintProtocol_proto_goTypes = []interface{}{
|
||||
(*LogicalFingerprint)(nil), // 0: textsecure.LogicalFingerprint
|
||||
(*CombinedFingerprints)(nil), // 1: textsecure.CombinedFingerprints
|
||||
}
|
||||
var file_serialize_FingerprintProtocol_proto_depIdxs = []int32{
|
||||
0, // 0: textsecure.CombinedFingerprints.localFingerprint:type_name -> textsecure.LogicalFingerprint
|
||||
0, // 1: textsecure.CombinedFingerprints.remoteFingerprint:type_name -> textsecure.LogicalFingerprint
|
||||
2, // [2:2] is the sub-list for method output_type
|
||||
2, // [2:2] is the sub-list for method input_type
|
||||
2, // [2:2] is the sub-list for extension type_name
|
||||
2, // [2:2] is the sub-list for extension extendee
|
||||
0, // [0:2] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_serialize_FingerprintProtocol_proto_init() }
|
||||
func file_serialize_FingerprintProtocol_proto_init() {
|
||||
if File_serialize_FingerprintProtocol_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_serialize_FingerprintProtocol_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*LogicalFingerprint); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_serialize_FingerprintProtocol_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*CombinedFingerprints); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_serialize_FingerprintProtocol_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_serialize_FingerprintProtocol_proto_goTypes,
|
||||
DependencyIndexes: file_serialize_FingerprintProtocol_proto_depIdxs,
|
||||
MessageInfos: file_serialize_FingerprintProtocol_proto_msgTypes,
|
||||
}.Build()
|
||||
File_serialize_FingerprintProtocol_proto = out.File
|
||||
file_serialize_FingerprintProtocol_proto_rawDesc = nil
|
||||
file_serialize_FingerprintProtocol_proto_goTypes = nil
|
||||
file_serialize_FingerprintProtocol_proto_depIdxs = nil
|
||||
}
|
||||
14
vendor/go.mau.fi/libsignal/serialize/FingerprintProtocol.proto
vendored
Normal file
14
vendor/go.mau.fi/libsignal/serialize/FingerprintProtocol.proto
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// From https://github.com/signalapp/libsignal-protocol-c/blob/master/protobuf/FingerprintProtocol.proto
|
||||
syntax = "proto2";
|
||||
package textsecure;
|
||||
|
||||
message LogicalFingerprint {
|
||||
optional bytes content = 1;
|
||||
optional bytes identifier = 2; // Version 0
|
||||
}
|
||||
|
||||
message CombinedFingerprints {
|
||||
optional uint32 version = 1;
|
||||
optional LogicalFingerprint localFingerprint = 2;
|
||||
optional LogicalFingerprint remoteFingerprint = 3;
|
||||
}
|
||||
303
vendor/go.mau.fi/libsignal/serialize/JSONSerializer.go
vendored
Normal file
303
vendor/go.mau.fi/libsignal/serialize/JSONSerializer.go
vendored
Normal file
@@ -0,0 +1,303 @@
|
||||
package serialize
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
groupRecord "go.mau.fi/libsignal/groups/state/record"
|
||||
"go.mau.fi/libsignal/logger"
|
||||
"go.mau.fi/libsignal/protocol"
|
||||
"go.mau.fi/libsignal/state/record"
|
||||
)
|
||||
|
||||
// NewJSONSerializer will return a serializer for all Signal objects that will
|
||||
// be responsible for converting objects to and from JSON bytes.
|
||||
func NewJSONSerializer() *Serializer {
|
||||
serializer := NewSerializer()
|
||||
|
||||
serializer.SignalMessage = &JSONSignalMessageSerializer{}
|
||||
serializer.PreKeySignalMessage = &JSONPreKeySignalMessageSerializer{}
|
||||
serializer.SignedPreKeyRecord = &JSONSignedPreKeyRecordSerializer{}
|
||||
serializer.PreKeyRecord = &JSONPreKeyRecordSerializer{}
|
||||
serializer.State = &JSONStateSerializer{}
|
||||
serializer.Session = &JSONSessionSerializer{}
|
||||
serializer.SenderKeyMessage = &JSONSenderKeyMessageSerializer{}
|
||||
serializer.SenderKeyDistributionMessage = &JSONSenderKeyDistributionMessageSerializer{}
|
||||
serializer.SenderKeyRecord = &JSONSenderKeySessionSerializer{}
|
||||
serializer.SenderKeyState = &JSONSenderKeyStateSerializer{}
|
||||
|
||||
return serializer
|
||||
}
|
||||
|
||||
// JSONSignalMessageSerializer is a structure for serializing signal messages into
|
||||
// and from JSON.
|
||||
type JSONSignalMessageSerializer struct{}
|
||||
|
||||
// Serialize will take a signal message structure and convert it to JSON bytes.
|
||||
func (j *JSONSignalMessageSerializer) Serialize(signalMessage *protocol.SignalMessageStructure) []byte {
|
||||
serialized, err := json.Marshal(*signalMessage)
|
||||
if err != nil {
|
||||
logger.Error("Error serializing signal message: ", err)
|
||||
}
|
||||
// logger.Debug("Serialize result: ", string(serialized))
|
||||
|
||||
return serialized
|
||||
}
|
||||
|
||||
// Deserialize will take in JSON bytes and return a signal message structure.
|
||||
func (j *JSONSignalMessageSerializer) Deserialize(serialized []byte) (*protocol.SignalMessageStructure, error) {
|
||||
var signalMessage protocol.SignalMessageStructure
|
||||
err := json.Unmarshal(serialized, &signalMessage)
|
||||
if err != nil {
|
||||
logger.Error("Error deserializing signal message: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &signalMessage, nil
|
||||
}
|
||||
|
||||
// JSONPreKeySignalMessageSerializer is a structure for serializing prekey signal messages
|
||||
// into and from JSON.
|
||||
type JSONPreKeySignalMessageSerializer struct{}
|
||||
|
||||
// Serialize will take a prekey signal message structure and convert it to JSON bytes.
|
||||
func (j *JSONPreKeySignalMessageSerializer) Serialize(signalMessage *protocol.PreKeySignalMessageStructure) []byte {
|
||||
serialized, err := json.Marshal(signalMessage)
|
||||
if err != nil {
|
||||
logger.Error("Error serializing prekey signal message: ", err)
|
||||
}
|
||||
// logger.Debug("Serialize result: ", string(serialized))
|
||||
|
||||
return serialized
|
||||
}
|
||||
|
||||
// Deserialize will take in JSON bytes and return a prekey signal message structure.
|
||||
func (j *JSONPreKeySignalMessageSerializer) Deserialize(serialized []byte) (*protocol.PreKeySignalMessageStructure, error) {
|
||||
var preKeySignalMessage protocol.PreKeySignalMessageStructure
|
||||
err := json.Unmarshal(serialized, &preKeySignalMessage)
|
||||
if err != nil {
|
||||
logger.Error("Error deserializing prekey signal message: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &preKeySignalMessage, nil
|
||||
}
|
||||
|
||||
// JSONSignedPreKeyRecordSerializer is a structure for serializing signed prekey records
|
||||
// into and from JSON.
|
||||
type JSONSignedPreKeyRecordSerializer struct{}
|
||||
|
||||
// Serialize will take a signed prekey record structure and convert it to JSON bytes.
|
||||
func (j *JSONSignedPreKeyRecordSerializer) Serialize(signedPreKey *record.SignedPreKeyStructure) []byte {
|
||||
serialized, err := json.Marshal(signedPreKey)
|
||||
if err != nil {
|
||||
logger.Error("Error serializing signed prekey record: ", err)
|
||||
}
|
||||
// logger.Debug("Serialize result: ", string(serialized))
|
||||
|
||||
return serialized
|
||||
}
|
||||
|
||||
// Deserialize will take in JSON bytes and return a signed prekey record structure.
|
||||
func (j *JSONSignedPreKeyRecordSerializer) Deserialize(serialized []byte) (*record.SignedPreKeyStructure, error) {
|
||||
var signedPreKeyStructure record.SignedPreKeyStructure
|
||||
err := json.Unmarshal(serialized, &signedPreKeyStructure)
|
||||
if err != nil {
|
||||
logger.Error("Error deserializing signed prekey record: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &signedPreKeyStructure, nil
|
||||
}
|
||||
|
||||
// JSONPreKeyRecordSerializer is a structure for serializing prekey records
|
||||
// into and from JSON.
|
||||
type JSONPreKeyRecordSerializer struct{}
|
||||
|
||||
// Serialize will take a prekey record structure and convert it to JSON bytes.
|
||||
func (j *JSONPreKeyRecordSerializer) Serialize(preKey *record.PreKeyStructure) []byte {
|
||||
serialized, err := json.Marshal(preKey)
|
||||
if err != nil {
|
||||
logger.Error("Error serializing prekey record: ", err)
|
||||
}
|
||||
// logger.Debug("Serialize result: ", string(serialized))
|
||||
|
||||
return serialized
|
||||
}
|
||||
|
||||
// Deserialize will take in JSON bytes and return a prekey record structure.
|
||||
func (j *JSONPreKeyRecordSerializer) Deserialize(serialized []byte) (*record.PreKeyStructure, error) {
|
||||
var preKeyStructure record.PreKeyStructure
|
||||
err := json.Unmarshal(serialized, &preKeyStructure)
|
||||
if err != nil {
|
||||
logger.Error("Error deserializing prekey record: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &preKeyStructure, nil
|
||||
}
|
||||
|
||||
// JSONStateSerializer is a structure for serializing session states into
|
||||
// and from JSON.
|
||||
type JSONStateSerializer struct{}
|
||||
|
||||
// Serialize will take a session state structure and convert it to JSON bytes.
|
||||
func (j *JSONStateSerializer) Serialize(state *record.StateStructure) []byte {
|
||||
serialized, err := json.Marshal(state)
|
||||
if err != nil {
|
||||
logger.Error("Error serializing session state: ", err)
|
||||
}
|
||||
logger.Debug("Serialize result: ", string(serialized))
|
||||
|
||||
return serialized
|
||||
}
|
||||
|
||||
// Deserialize will take in JSON bytes and return a session state structure.
|
||||
func (j *JSONStateSerializer) Deserialize(serialized []byte) (*record.StateStructure, error) {
|
||||
var stateStructure record.StateStructure
|
||||
err := json.Unmarshal(serialized, &stateStructure)
|
||||
if err != nil {
|
||||
logger.Error("Error deserializing session state: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &stateStructure, nil
|
||||
}
|
||||
|
||||
// JSONSessionSerializer is a structure for serializing session records into
|
||||
// and from JSON.
|
||||
type JSONSessionSerializer struct{}
|
||||
|
||||
// Serialize will take a session structure and convert it to JSON bytes.
|
||||
func (j *JSONSessionSerializer) Serialize(session *record.SessionStructure) []byte {
|
||||
serialized, err := json.Marshal(session)
|
||||
if err != nil {
|
||||
logger.Error("Error serializing session: ", err)
|
||||
}
|
||||
// logger.Debug("Serialize result: ", string(serialized))
|
||||
|
||||
return serialized
|
||||
}
|
||||
|
||||
// Deserialize will take in JSON bytes and return a session structure, which can be
|
||||
// used to create a new Session Record object.
|
||||
func (j *JSONSessionSerializer) Deserialize(serialized []byte) (*record.SessionStructure, error) {
|
||||
var sessionStructure record.SessionStructure
|
||||
err := json.Unmarshal(serialized, &sessionStructure)
|
||||
if err != nil {
|
||||
logger.Error("Error deserializing session: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &sessionStructure, nil
|
||||
}
|
||||
|
||||
// JSONSenderKeyDistributionMessageSerializer is a structure for serializing senderkey
|
||||
// distribution records to and from JSON.
|
||||
type JSONSenderKeyDistributionMessageSerializer struct{}
|
||||
|
||||
// Serialize will take a senderkey distribution message and convert it to JSON bytes.
|
||||
func (j *JSONSenderKeyDistributionMessageSerializer) Serialize(message *protocol.SenderKeyDistributionMessageStructure) []byte {
|
||||
serialized, err := json.Marshal(message)
|
||||
if err != nil {
|
||||
logger.Error("Error serializing senderkey distribution message: ", err)
|
||||
}
|
||||
// logger.Debug("Serialize result: ", string(serialized))
|
||||
|
||||
return serialized
|
||||
}
|
||||
|
||||
// Deserialize will take in JSON bytes and return a message structure, which can be
|
||||
// used to create a new SenderKey Distribution object.
|
||||
func (j *JSONSenderKeyDistributionMessageSerializer) Deserialize(serialized []byte) (*protocol.SenderKeyDistributionMessageStructure, error) {
|
||||
var msgStructure protocol.SenderKeyDistributionMessageStructure
|
||||
err := json.Unmarshal(serialized, &msgStructure)
|
||||
if err != nil {
|
||||
logger.Error("Error deserializing senderkey distribution message: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &msgStructure, nil
|
||||
}
|
||||
|
||||
// JSONSenderKeyMessageSerializer is a structure for serializing senderkey
|
||||
// messages to and from JSON.
|
||||
type JSONSenderKeyMessageSerializer struct{}
|
||||
|
||||
// Serialize will take a senderkey message and convert it to JSON bytes.
|
||||
func (j *JSONSenderKeyMessageSerializer) Serialize(message *protocol.SenderKeyMessageStructure) []byte {
|
||||
serialized, err := json.Marshal(message)
|
||||
if err != nil {
|
||||
logger.Error("Error serializing senderkey distribution message: ", err)
|
||||
}
|
||||
// logger.Debug("Serialize result: ", string(serialized))
|
||||
|
||||
return serialized
|
||||
}
|
||||
|
||||
// Deserialize will take in JSON bytes and return a message structure, which can be
|
||||
// used to create a new SenderKey message object.
|
||||
func (j *JSONSenderKeyMessageSerializer) Deserialize(serialized []byte) (*protocol.SenderKeyMessageStructure, error) {
|
||||
var msgStructure protocol.SenderKeyMessageStructure
|
||||
err := json.Unmarshal(serialized, &msgStructure)
|
||||
if err != nil {
|
||||
logger.Error("Error deserializing senderkey message: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &msgStructure, nil
|
||||
}
|
||||
|
||||
// JSONSenderKeyStateSerializer is a structure for serializing group session states into
|
||||
// and from JSON.
|
||||
type JSONSenderKeyStateSerializer struct{}
|
||||
|
||||
// Serialize will take a session state structure and convert it to JSON bytes.
|
||||
func (j *JSONSenderKeyStateSerializer) Serialize(state *groupRecord.SenderKeyStateStructure) []byte {
|
||||
serialized, err := json.Marshal(state)
|
||||
if err != nil {
|
||||
logger.Error("Error serializing session state: ", err)
|
||||
}
|
||||
// logger.Debug("Serialize result: ", string(serialized))
|
||||
|
||||
return serialized
|
||||
}
|
||||
|
||||
// Deserialize will take in JSON bytes and return a session state structure.
|
||||
func (j *JSONSenderKeyStateSerializer) Deserialize(serialized []byte) (*groupRecord.SenderKeyStateStructure, error) {
|
||||
var stateStructure groupRecord.SenderKeyStateStructure
|
||||
err := json.Unmarshal(serialized, &stateStructure)
|
||||
if err != nil {
|
||||
logger.Error("Error deserializing session state: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &stateStructure, nil
|
||||
}
|
||||
|
||||
// JSONSenderKeySessionSerializer is a structure for serializing session records into
|
||||
// and from JSON.
|
||||
type JSONSenderKeySessionSerializer struct{}
|
||||
|
||||
// Serialize will take a session structure and convert it to JSON bytes.
|
||||
func (j *JSONSenderKeySessionSerializer) Serialize(session *groupRecord.SenderKeyStructure) []byte {
|
||||
serialized, err := json.Marshal(session)
|
||||
if err != nil {
|
||||
logger.Error("Error serializing session: ", err)
|
||||
}
|
||||
// logger.Debug("Serialize result: ", string(serialized))
|
||||
|
||||
return serialized
|
||||
}
|
||||
|
||||
// Deserialize will take in JSON bytes and return a session structure, which can be
|
||||
// used to create a new Session Record object.
|
||||
func (j *JSONSenderKeySessionSerializer) Deserialize(serialized []byte) (*groupRecord.SenderKeyStructure, error) {
|
||||
var sessionStructure groupRecord.SenderKeyStructure
|
||||
err := json.Unmarshal(serialized, &sessionStructure)
|
||||
if err != nil {
|
||||
logger.Error("Error deserializing session: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &sessionStructure, nil
|
||||
}
|
||||
1500
vendor/go.mau.fi/libsignal/serialize/LocalStorageProtocol.pb.go
vendored
Normal file
1500
vendor/go.mau.fi/libsignal/serialize/LocalStorageProtocol.pb.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
114
vendor/go.mau.fi/libsignal/serialize/LocalStorageProtocol.proto
vendored
Normal file
114
vendor/go.mau.fi/libsignal/serialize/LocalStorageProtocol.proto
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
// From https://github.com/signalapp/libsignal-protocol-c/blob/master/protobuf/LocalStorageProtocol.proto
|
||||
syntax = "proto2";
|
||||
package textsecure;
|
||||
|
||||
option java_package = "org.whispersystems.libsignal.state";
|
||||
option java_outer_classname = "StorageProtos";
|
||||
|
||||
message SessionStructure {
|
||||
message Chain {
|
||||
optional bytes senderRatchetKey = 1;
|
||||
optional bytes senderRatchetKeyPrivate = 2;
|
||||
|
||||
message ChainKey {
|
||||
optional uint32 index = 1;
|
||||
optional bytes key = 2;
|
||||
}
|
||||
|
||||
optional ChainKey chainKey = 3;
|
||||
|
||||
message MessageKey {
|
||||
optional uint32 index = 1;
|
||||
optional bytes cipherKey = 2;
|
||||
optional bytes macKey = 3;
|
||||
optional bytes iv = 4;
|
||||
}
|
||||
|
||||
repeated MessageKey messageKeys = 4;
|
||||
}
|
||||
|
||||
message PendingKeyExchange {
|
||||
optional uint32 sequence = 1;
|
||||
optional bytes localBaseKey = 2;
|
||||
optional bytes localBaseKeyPrivate = 3;
|
||||
optional bytes localRatchetKey = 4;
|
||||
optional bytes localRatchetKeyPrivate = 5;
|
||||
optional bytes localIdentityKey = 7;
|
||||
optional bytes localIdentityKeyPrivate = 8;
|
||||
}
|
||||
|
||||
message PendingPreKey {
|
||||
optional uint32 preKeyId = 1;
|
||||
optional int32 signedPreKeyId = 3;
|
||||
optional bytes baseKey = 2;
|
||||
}
|
||||
|
||||
optional uint32 sessionVersion = 1;
|
||||
optional bytes localIdentityPublic = 2;
|
||||
optional bytes remoteIdentityPublic = 3;
|
||||
|
||||
optional bytes rootKey = 4;
|
||||
optional uint32 previousCounter = 5;
|
||||
|
||||
optional Chain senderChain = 6;
|
||||
repeated Chain receiverChains = 7;
|
||||
|
||||
optional PendingKeyExchange pendingKeyExchange = 8;
|
||||
optional PendingPreKey pendingPreKey = 9;
|
||||
|
||||
optional uint32 remoteRegistrationId = 10;
|
||||
optional uint32 localRegistrationId = 11;
|
||||
|
||||
optional bool needsRefresh = 12;
|
||||
optional bytes aliceBaseKey = 13;
|
||||
}
|
||||
|
||||
message RecordStructure {
|
||||
optional SessionStructure currentSession = 1;
|
||||
repeated SessionStructure previousSessions = 2;
|
||||
}
|
||||
|
||||
message PreKeyRecordStructure {
|
||||
optional uint32 id = 1;
|
||||
optional bytes publicKey = 2;
|
||||
optional bytes privateKey = 3;
|
||||
}
|
||||
|
||||
message SignedPreKeyRecordStructure {
|
||||
optional uint32 id = 1;
|
||||
optional bytes publicKey = 2;
|
||||
optional bytes privateKey = 3;
|
||||
optional bytes signature = 4;
|
||||
optional fixed64 timestamp = 5;
|
||||
}
|
||||
|
||||
message IdentityKeyPairStructure {
|
||||
optional bytes publicKey = 1;
|
||||
optional bytes privateKey = 2;
|
||||
}
|
||||
|
||||
message SenderKeyStateStructure {
|
||||
message SenderChainKey {
|
||||
optional uint32 iteration = 1;
|
||||
optional bytes seed = 2;
|
||||
}
|
||||
|
||||
message SenderMessageKey {
|
||||
optional uint32 iteration = 1;
|
||||
optional bytes seed = 2;
|
||||
}
|
||||
|
||||
message SenderSigningKey {
|
||||
optional bytes public = 1;
|
||||
optional bytes private = 2;
|
||||
}
|
||||
|
||||
optional uint32 senderKeyId = 1;
|
||||
optional SenderChainKey senderChainKey = 2;
|
||||
optional SenderSigningKey senderSigningKey = 3;
|
||||
repeated SenderMessageKey senderMessageKeys = 4;
|
||||
}
|
||||
|
||||
message SenderKeyRecordStructure {
|
||||
repeated SenderKeyStateStructure senderKeyStates = 1;
|
||||
}
|
||||
262
vendor/go.mau.fi/libsignal/serialize/ProtoBufferSerializer.go
vendored
Normal file
262
vendor/go.mau.fi/libsignal/serialize/ProtoBufferSerializer.go
vendored
Normal file
@@ -0,0 +1,262 @@
|
||||
package serialize
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"go.mau.fi/libsignal/logger"
|
||||
"go.mau.fi/libsignal/protocol"
|
||||
"go.mau.fi/libsignal/util/bytehelper"
|
||||
"go.mau.fi/libsignal/util/optional"
|
||||
proto "google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// NewProtoBufSerializer will return a serializer for all Signal objects that will
|
||||
// be responsible for converting objects to and from ProtoBuf bytes.
|
||||
func NewProtoBufSerializer() *Serializer {
|
||||
serializer := NewSerializer()
|
||||
|
||||
serializer.SignalMessage = &ProtoBufSignalMessageSerializer{}
|
||||
serializer.PreKeySignalMessage = &ProtoBufPreKeySignalMessageSerializer{}
|
||||
serializer.SenderKeyMessage = &ProtoBufSenderKeyMessageSerializer{}
|
||||
serializer.SenderKeyDistributionMessage = &ProtoBufSenderKeyDistributionMessageSerializer{}
|
||||
serializer.SignedPreKeyRecord = &JSONSignedPreKeyRecordSerializer{}
|
||||
serializer.PreKeyRecord = &JSONPreKeyRecordSerializer{}
|
||||
serializer.State = &JSONStateSerializer{}
|
||||
serializer.Session = &JSONSessionSerializer{}
|
||||
serializer.SenderKeyRecord = &JSONSenderKeySessionSerializer{}
|
||||
serializer.SenderKeyState = &JSONSenderKeyStateSerializer{}
|
||||
|
||||
return serializer
|
||||
}
|
||||
|
||||
func highBitsToInt(value byte) int {
|
||||
return int((value & 0xFF) >> 4)
|
||||
}
|
||||
|
||||
func intsToByteHighAndLow(highValue, lowValue int) byte {
|
||||
return byte((highValue<<4 | lowValue) & 0xFF)
|
||||
}
|
||||
|
||||
// ProtoBufSignalMessageSerializer is a structure for serializing signal messages into
|
||||
// and from ProtoBuf.
|
||||
type ProtoBufSignalMessageSerializer struct{}
|
||||
|
||||
// Serialize will take a signal message structure and convert it to ProtoBuf bytes.
|
||||
func (j *ProtoBufSignalMessageSerializer) Serialize(signalMessage *protocol.SignalMessageStructure) []byte {
|
||||
sm := &SignalMessage{
|
||||
RatchetKey: signalMessage.RatchetKey,
|
||||
Counter: &signalMessage.Counter,
|
||||
PreviousCounter: &signalMessage.PreviousCounter,
|
||||
Ciphertext: signalMessage.CipherText,
|
||||
}
|
||||
var serialized []byte
|
||||
message, err := proto.Marshal(sm)
|
||||
if err != nil {
|
||||
logger.Error("Error serializing signal message: ", err)
|
||||
}
|
||||
|
||||
if signalMessage.Version != 0 {
|
||||
serialized = append(serialized, []byte(strconv.Itoa(signalMessage.Version))...)
|
||||
}
|
||||
serialized = append(serialized, message...)
|
||||
|
||||
if signalMessage.Mac != nil {
|
||||
serialized = append(serialized, signalMessage.Mac...)
|
||||
}
|
||||
|
||||
return serialized
|
||||
}
|
||||
|
||||
// Deserialize will take in ProtoBuf bytes and return a signal message structure.
|
||||
func (j *ProtoBufSignalMessageSerializer) Deserialize(serialized []byte) (*protocol.SignalMessageStructure, error) {
|
||||
parts, err := bytehelper.SplitThree(serialized, 1, len(serialized)-1-protocol.MacLength, protocol.MacLength)
|
||||
if err != nil {
|
||||
logger.Error("Error split signal message: ", err)
|
||||
return nil, err
|
||||
}
|
||||
version := highBitsToInt(parts[0][0])
|
||||
message := parts[1]
|
||||
mac := parts[2]
|
||||
|
||||
var sm SignalMessage
|
||||
err = proto.Unmarshal(message, &sm)
|
||||
if err != nil {
|
||||
logger.Error("Error deserializing signal message: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
signalMessage := protocol.SignalMessageStructure{
|
||||
Version: version,
|
||||
RatchetKey: sm.GetRatchetKey(),
|
||||
Counter: sm.GetCounter(),
|
||||
PreviousCounter: sm.GetPreviousCounter(),
|
||||
CipherText: sm.GetCiphertext(),
|
||||
Mac: mac,
|
||||
}
|
||||
|
||||
return &signalMessage, nil
|
||||
}
|
||||
|
||||
// ProtoBufPreKeySignalMessageSerializer is a structure for serializing prekey signal messages
|
||||
// into and from ProtoBuf.
|
||||
type ProtoBufPreKeySignalMessageSerializer struct{}
|
||||
|
||||
// Serialize will take a prekey signal message structure and convert it to ProtoBuf bytes.
|
||||
func (j *ProtoBufPreKeySignalMessageSerializer) Serialize(signalMessage *protocol.PreKeySignalMessageStructure) []byte {
|
||||
preKeyMessage := &PreKeySignalMessage{
|
||||
RegistrationId: &signalMessage.RegistrationID,
|
||||
SignedPreKeyId: &signalMessage.SignedPreKeyID,
|
||||
BaseKey: signalMessage.BaseKey,
|
||||
IdentityKey: signalMessage.IdentityKey,
|
||||
Message: signalMessage.Message,
|
||||
}
|
||||
|
||||
if !signalMessage.PreKeyID.IsEmpty {
|
||||
preKeyMessage.PreKeyId = &signalMessage.PreKeyID.Value
|
||||
}
|
||||
|
||||
message, err := proto.Marshal(preKeyMessage)
|
||||
if err != nil {
|
||||
logger.Error("Error serializing prekey signal message: ", err)
|
||||
}
|
||||
|
||||
serialized := append([]byte(strconv.Itoa(signalMessage.Version)), message...)
|
||||
logger.Debug("Serialize PreKeySignalMessage result: ", serialized)
|
||||
return serialized
|
||||
}
|
||||
|
||||
// Deserialize will take in ProtoBuf bytes and return a prekey signal message structure.
|
||||
func (j *ProtoBufPreKeySignalMessageSerializer) Deserialize(serialized []byte) (*protocol.PreKeySignalMessageStructure, error) {
|
||||
version := highBitsToInt(serialized[0])
|
||||
message := serialized[1:]
|
||||
var sm PreKeySignalMessage
|
||||
err := proto.Unmarshal(message, &sm)
|
||||
if err != nil {
|
||||
logger.Error("Error deserializing prekey signal message: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
preKeyId := optional.NewEmptyUint32()
|
||||
if sm.GetPreKeyId() != 0 {
|
||||
preKeyId = optional.NewOptionalUint32(sm.GetPreKeyId())
|
||||
}
|
||||
|
||||
preKeySignalMessage := protocol.PreKeySignalMessageStructure{
|
||||
Version: version,
|
||||
RegistrationID: sm.GetRegistrationId(),
|
||||
BaseKey: sm.GetBaseKey(),
|
||||
IdentityKey: sm.GetIdentityKey(),
|
||||
SignedPreKeyID: sm.GetSignedPreKeyId(),
|
||||
Message: sm.GetMessage(),
|
||||
PreKeyID: preKeyId,
|
||||
}
|
||||
|
||||
return &preKeySignalMessage, nil
|
||||
}
|
||||
|
||||
// ProtoBufSenderKeyDistributionMessageSerializer is a structure for serializing senderkey
|
||||
// distribution records to and from ProtoBuf.
|
||||
type ProtoBufSenderKeyDistributionMessageSerializer struct{}
|
||||
|
||||
// Serialize will take a senderkey distribution message and convert it to ProtoBuf bytes.
|
||||
func (j *ProtoBufSenderKeyDistributionMessageSerializer) Serialize(message *protocol.SenderKeyDistributionMessageStructure) []byte {
|
||||
senderDis := SenderKeyDistributionMessage{
|
||||
Id: &message.ID,
|
||||
Iteration: &message.Iteration,
|
||||
ChainKey: message.ChainKey,
|
||||
SigningKey: message.SigningKey,
|
||||
}
|
||||
|
||||
serialized, err := proto.Marshal(&senderDis)
|
||||
if err != nil {
|
||||
logger.Error("Error serializing senderkey distribution message: ", err)
|
||||
}
|
||||
|
||||
version := strconv.Itoa(int(message.Version))
|
||||
serialized = append([]byte(version), serialized...)
|
||||
logger.Debug("Serialize result: ", serialized)
|
||||
return serialized
|
||||
}
|
||||
|
||||
// Deserialize will take in ProtoBuf bytes and return a message structure, which can be
|
||||
// used to create a new SenderKey Distribution object.
|
||||
func (j *ProtoBufSenderKeyDistributionMessageSerializer) Deserialize(serialized []byte) (*protocol.SenderKeyDistributionMessageStructure, error) {
|
||||
version := uint32(highBitsToInt(serialized[0]))
|
||||
message := serialized[1:]
|
||||
|
||||
var senderKeyDis SenderKeyDistributionMessage
|
||||
err := proto.Unmarshal(message, &senderKeyDis)
|
||||
if err != nil {
|
||||
logger.Error("Error deserializing senderkey distribution message: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msgStructure := protocol.SenderKeyDistributionMessageStructure{
|
||||
ID: senderKeyDis.GetId(),
|
||||
Iteration: senderKeyDis.GetIteration(),
|
||||
ChainKey: senderKeyDis.GetChainKey(),
|
||||
SigningKey: senderKeyDis.GetSigningKey(),
|
||||
Version: version,
|
||||
}
|
||||
return &msgStructure, nil
|
||||
}
|
||||
|
||||
// ProtoBufSenderKeyMessageSerializer is a structure for serializing senderkey
|
||||
// messages to and from ProtoBuf.
|
||||
type ProtoBufSenderKeyMessageSerializer struct{}
|
||||
|
||||
// Serialize will take a senderkey message and convert it to ProtoBuf bytes.
|
||||
func (j *ProtoBufSenderKeyMessageSerializer) Serialize(message *protocol.SenderKeyMessageStructure) []byte {
|
||||
senderMessage := &SenderKeyMessage{
|
||||
Id: &message.ID,
|
||||
Iteration: &message.Iteration,
|
||||
Ciphertext: message.CipherText,
|
||||
}
|
||||
|
||||
var serialized []byte
|
||||
m, err := proto.Marshal(senderMessage)
|
||||
if err != nil {
|
||||
logger.Error("Error serializing signal message: ", err)
|
||||
}
|
||||
|
||||
if message.Version != 0 {
|
||||
serialized = append([]byte(fmt.Sprint(message.Version)), m...)
|
||||
}
|
||||
|
||||
if message.Signature != nil {
|
||||
serialized = append(serialized, message.Signature...)
|
||||
}
|
||||
logger.Debug("Serialize result: ", serialized)
|
||||
return serialized
|
||||
}
|
||||
|
||||
// Deserialize will take in ProtoBuf bytes and return a message structure, which can be
|
||||
// used to create a new SenderKey message object.
|
||||
func (j *ProtoBufSenderKeyMessageSerializer) Deserialize(serialized []byte) (*protocol.SenderKeyMessageStructure, error) {
|
||||
parts, err := bytehelper.SplitThree(serialized, 1, len(serialized)-1-64, 64)
|
||||
if err != nil {
|
||||
logger.Error("Error split signal message: ", err)
|
||||
return nil, err
|
||||
}
|
||||
version := uint32(highBitsToInt(parts[0][0]))
|
||||
message := parts[1]
|
||||
signature := parts[2]
|
||||
|
||||
var senderKey SenderKeyMessage
|
||||
err = proto.Unmarshal(message, &senderKey)
|
||||
if err != nil {
|
||||
logger.Error("Error deserializing senderkey message: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
msgStructure := protocol.SenderKeyMessageStructure{
|
||||
Version: version,
|
||||
ID: senderKey.GetId(),
|
||||
Iteration: senderKey.GetIteration(),
|
||||
CipherText: senderKey.GetCiphertext(),
|
||||
Signature: signature,
|
||||
}
|
||||
|
||||
return &msgStructure, nil
|
||||
}
|
||||
31
vendor/go.mau.fi/libsignal/serialize/Serializer.go
vendored
Normal file
31
vendor/go.mau.fi/libsignal/serialize/Serializer.go
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// Package serialize provides a serialization structure to serialize and
|
||||
// deserialize Signal objects into storeable and transportable bytes.
|
||||
package serialize
|
||||
|
||||
import (
|
||||
groupRecord "go.mau.fi/libsignal/groups/state/record"
|
||||
"go.mau.fi/libsignal/protocol"
|
||||
"go.mau.fi/libsignal/state/record"
|
||||
)
|
||||
|
||||
// NewSerializer will return a new serializer object that will be used
|
||||
// to encode/decode Signal objects into bytes.
|
||||
func NewSerializer() *Serializer {
|
||||
return &Serializer{}
|
||||
}
|
||||
|
||||
// Serializer is a structure to serialize Signal objects
|
||||
// into bytes. This allows you to use any serialization format
|
||||
// to store or send Signal objects.
|
||||
type Serializer struct {
|
||||
SenderKeyRecord groupRecord.SenderKeySerializer
|
||||
SenderKeyState groupRecord.SenderKeyStateSerializer
|
||||
SignalMessage protocol.SignalMessageSerializer
|
||||
PreKeySignalMessage protocol.PreKeySignalMessageSerializer
|
||||
SenderKeyMessage protocol.SenderKeyMessageSerializer
|
||||
SenderKeyDistributionMessage protocol.SenderKeyDistributionMessageSerializer
|
||||
SignedPreKeyRecord record.SignedPreKeySerializer
|
||||
PreKeyRecord record.PreKeySerializer
|
||||
State record.StateSerializer
|
||||
Session record.SessionSerializer
|
||||
}
|
||||
640
vendor/go.mau.fi/libsignal/serialize/WhisperTextProtocol.pb.go
vendored
Normal file
640
vendor/go.mau.fi/libsignal/serialize/WhisperTextProtocol.pb.go
vendored
Normal file
@@ -0,0 +1,640 @@
|
||||
// From https://github.com/signalapp/libsignal-protocol-c/blob/master/protobuf/WhisperTextProtocol.proto
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// versions:
|
||||
// protoc-gen-go v1.26.0
|
||||
// protoc v3.12.4
|
||||
// source: serialize/WhisperTextProtocol.proto
|
||||
|
||||
package serialize
|
||||
|
||||
import (
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type SignalMessage struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
RatchetKey []byte `protobuf:"bytes,1,opt,name=ratchetKey" json:"ratchetKey,omitempty"`
|
||||
Counter *uint32 `protobuf:"varint,2,opt,name=counter" json:"counter,omitempty"`
|
||||
PreviousCounter *uint32 `protobuf:"varint,3,opt,name=previousCounter" json:"previousCounter,omitempty"`
|
||||
Ciphertext []byte `protobuf:"bytes,4,opt,name=ciphertext" json:"ciphertext,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SignalMessage) Reset() {
|
||||
*x = SignalMessage{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_serialize_WhisperTextProtocol_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SignalMessage) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SignalMessage) ProtoMessage() {}
|
||||
|
||||
func (x *SignalMessage) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_serialize_WhisperTextProtocol_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SignalMessage.ProtoReflect.Descriptor instead.
|
||||
func (*SignalMessage) Descriptor() ([]byte, []int) {
|
||||
return file_serialize_WhisperTextProtocol_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
func (x *SignalMessage) GetRatchetKey() []byte {
|
||||
if x != nil {
|
||||
return x.RatchetKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SignalMessage) GetCounter() uint32 {
|
||||
if x != nil && x.Counter != nil {
|
||||
return *x.Counter
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SignalMessage) GetPreviousCounter() uint32 {
|
||||
if x != nil && x.PreviousCounter != nil {
|
||||
return *x.PreviousCounter
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SignalMessage) GetCiphertext() []byte {
|
||||
if x != nil {
|
||||
return x.Ciphertext
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type PreKeySignalMessage struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
RegistrationId *uint32 `protobuf:"varint,5,opt,name=registrationId" json:"registrationId,omitempty"`
|
||||
PreKeyId *uint32 `protobuf:"varint,1,opt,name=preKeyId" json:"preKeyId,omitempty"`
|
||||
SignedPreKeyId *uint32 `protobuf:"varint,6,opt,name=signedPreKeyId" json:"signedPreKeyId,omitempty"`
|
||||
BaseKey []byte `protobuf:"bytes,2,opt,name=baseKey" json:"baseKey,omitempty"`
|
||||
IdentityKey []byte `protobuf:"bytes,3,opt,name=identityKey" json:"identityKey,omitempty"`
|
||||
Message []byte `protobuf:"bytes,4,opt,name=message" json:"message,omitempty"` // SignalMessage
|
||||
}
|
||||
|
||||
func (x *PreKeySignalMessage) Reset() {
|
||||
*x = PreKeySignalMessage{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_serialize_WhisperTextProtocol_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *PreKeySignalMessage) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*PreKeySignalMessage) ProtoMessage() {}
|
||||
|
||||
func (x *PreKeySignalMessage) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_serialize_WhisperTextProtocol_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use PreKeySignalMessage.ProtoReflect.Descriptor instead.
|
||||
func (*PreKeySignalMessage) Descriptor() ([]byte, []int) {
|
||||
return file_serialize_WhisperTextProtocol_proto_rawDescGZIP(), []int{1}
|
||||
}
|
||||
|
||||
func (x *PreKeySignalMessage) GetRegistrationId() uint32 {
|
||||
if x != nil && x.RegistrationId != nil {
|
||||
return *x.RegistrationId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *PreKeySignalMessage) GetPreKeyId() uint32 {
|
||||
if x != nil && x.PreKeyId != nil {
|
||||
return *x.PreKeyId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *PreKeySignalMessage) GetSignedPreKeyId() uint32 {
|
||||
if x != nil && x.SignedPreKeyId != nil {
|
||||
return *x.SignedPreKeyId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *PreKeySignalMessage) GetBaseKey() []byte {
|
||||
if x != nil {
|
||||
return x.BaseKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PreKeySignalMessage) GetIdentityKey() []byte {
|
||||
if x != nil {
|
||||
return x.IdentityKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *PreKeySignalMessage) GetMessage() []byte {
|
||||
if x != nil {
|
||||
return x.Message
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type KeyExchangeMessage struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Id *uint32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"`
|
||||
BaseKey []byte `protobuf:"bytes,2,opt,name=baseKey" json:"baseKey,omitempty"`
|
||||
RatchetKey []byte `protobuf:"bytes,3,opt,name=ratchetKey" json:"ratchetKey,omitempty"`
|
||||
IdentityKey []byte `protobuf:"bytes,4,opt,name=identityKey" json:"identityKey,omitempty"`
|
||||
BaseKeySignature []byte `protobuf:"bytes,5,opt,name=baseKeySignature" json:"baseKeySignature,omitempty"`
|
||||
}
|
||||
|
||||
func (x *KeyExchangeMessage) Reset() {
|
||||
*x = KeyExchangeMessage{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_serialize_WhisperTextProtocol_proto_msgTypes[2]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *KeyExchangeMessage) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*KeyExchangeMessage) ProtoMessage() {}
|
||||
|
||||
func (x *KeyExchangeMessage) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_serialize_WhisperTextProtocol_proto_msgTypes[2]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use KeyExchangeMessage.ProtoReflect.Descriptor instead.
|
||||
func (*KeyExchangeMessage) Descriptor() ([]byte, []int) {
|
||||
return file_serialize_WhisperTextProtocol_proto_rawDescGZIP(), []int{2}
|
||||
}
|
||||
|
||||
func (x *KeyExchangeMessage) GetId() uint32 {
|
||||
if x != nil && x.Id != nil {
|
||||
return *x.Id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *KeyExchangeMessage) GetBaseKey() []byte {
|
||||
if x != nil {
|
||||
return x.BaseKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *KeyExchangeMessage) GetRatchetKey() []byte {
|
||||
if x != nil {
|
||||
return x.RatchetKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *KeyExchangeMessage) GetIdentityKey() []byte {
|
||||
if x != nil {
|
||||
return x.IdentityKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *KeyExchangeMessage) GetBaseKeySignature() []byte {
|
||||
if x != nil {
|
||||
return x.BaseKeySignature
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SenderKeyMessage struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Id *uint32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"`
|
||||
Iteration *uint32 `protobuf:"varint,2,opt,name=iteration" json:"iteration,omitempty"`
|
||||
Ciphertext []byte `protobuf:"bytes,3,opt,name=ciphertext" json:"ciphertext,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SenderKeyMessage) Reset() {
|
||||
*x = SenderKeyMessage{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_serialize_WhisperTextProtocol_proto_msgTypes[3]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SenderKeyMessage) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SenderKeyMessage) ProtoMessage() {}
|
||||
|
||||
func (x *SenderKeyMessage) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_serialize_WhisperTextProtocol_proto_msgTypes[3]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SenderKeyMessage.ProtoReflect.Descriptor instead.
|
||||
func (*SenderKeyMessage) Descriptor() ([]byte, []int) {
|
||||
return file_serialize_WhisperTextProtocol_proto_rawDescGZIP(), []int{3}
|
||||
}
|
||||
|
||||
func (x *SenderKeyMessage) GetId() uint32 {
|
||||
if x != nil && x.Id != nil {
|
||||
return *x.Id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SenderKeyMessage) GetIteration() uint32 {
|
||||
if x != nil && x.Iteration != nil {
|
||||
return *x.Iteration
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SenderKeyMessage) GetCiphertext() []byte {
|
||||
if x != nil {
|
||||
return x.Ciphertext
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type SenderKeyDistributionMessage struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Id *uint32 `protobuf:"varint,1,opt,name=id" json:"id,omitempty"`
|
||||
Iteration *uint32 `protobuf:"varint,2,opt,name=iteration" json:"iteration,omitempty"`
|
||||
ChainKey []byte `protobuf:"bytes,3,opt,name=chainKey" json:"chainKey,omitempty"`
|
||||
SigningKey []byte `protobuf:"bytes,4,opt,name=signingKey" json:"signingKey,omitempty"`
|
||||
}
|
||||
|
||||
func (x *SenderKeyDistributionMessage) Reset() {
|
||||
*x = SenderKeyDistributionMessage{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_serialize_WhisperTextProtocol_proto_msgTypes[4]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *SenderKeyDistributionMessage) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*SenderKeyDistributionMessage) ProtoMessage() {}
|
||||
|
||||
func (x *SenderKeyDistributionMessage) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_serialize_WhisperTextProtocol_proto_msgTypes[4]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use SenderKeyDistributionMessage.ProtoReflect.Descriptor instead.
|
||||
func (*SenderKeyDistributionMessage) Descriptor() ([]byte, []int) {
|
||||
return file_serialize_WhisperTextProtocol_proto_rawDescGZIP(), []int{4}
|
||||
}
|
||||
|
||||
func (x *SenderKeyDistributionMessage) GetId() uint32 {
|
||||
if x != nil && x.Id != nil {
|
||||
return *x.Id
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SenderKeyDistributionMessage) GetIteration() uint32 {
|
||||
if x != nil && x.Iteration != nil {
|
||||
return *x.Iteration
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *SenderKeyDistributionMessage) GetChainKey() []byte {
|
||||
if x != nil {
|
||||
return x.ChainKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *SenderKeyDistributionMessage) GetSigningKey() []byte {
|
||||
if x != nil {
|
||||
return x.SigningKey
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DeviceConsistencyCodeMessage struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Generation *uint32 `protobuf:"varint,1,opt,name=generation" json:"generation,omitempty"`
|
||||
Signature []byte `protobuf:"bytes,2,opt,name=signature" json:"signature,omitempty"`
|
||||
}
|
||||
|
||||
func (x *DeviceConsistencyCodeMessage) Reset() {
|
||||
*x = DeviceConsistencyCodeMessage{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_serialize_WhisperTextProtocol_proto_msgTypes[5]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *DeviceConsistencyCodeMessage) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*DeviceConsistencyCodeMessage) ProtoMessage() {}
|
||||
|
||||
func (x *DeviceConsistencyCodeMessage) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_serialize_WhisperTextProtocol_proto_msgTypes[5]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use DeviceConsistencyCodeMessage.ProtoReflect.Descriptor instead.
|
||||
func (*DeviceConsistencyCodeMessage) Descriptor() ([]byte, []int) {
|
||||
return file_serialize_WhisperTextProtocol_proto_rawDescGZIP(), []int{5}
|
||||
}
|
||||
|
||||
func (x *DeviceConsistencyCodeMessage) GetGeneration() uint32 {
|
||||
if x != nil && x.Generation != nil {
|
||||
return *x.Generation
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *DeviceConsistencyCodeMessage) GetSignature() []byte {
|
||||
if x != nil {
|
||||
return x.Signature
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_serialize_WhisperTextProtocol_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_serialize_WhisperTextProtocol_proto_rawDesc = []byte{
|
||||
0x0a, 0x23, 0x73, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x69, 0x7a, 0x65, 0x2f, 0x57, 0x68, 0x69, 0x73,
|
||||
0x70, 0x65, 0x72, 0x54, 0x65, 0x78, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2e,
|
||||
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0a, 0x74, 0x65, 0x78, 0x74, 0x73, 0x65, 0x63, 0x75, 0x72,
|
||||
0x65, 0x22, 0x93, 0x01, 0x0a, 0x0d, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x4d, 0x65, 0x73, 0x73,
|
||||
0x61, 0x67, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x61, 0x74, 0x63, 0x68, 0x65, 0x74, 0x4b, 0x65,
|
||||
0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x72, 0x61, 0x74, 0x63, 0x68, 0x65, 0x74,
|
||||
0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x18, 0x02,
|
||||
0x20, 0x01, 0x28, 0x0d, 0x52, 0x07, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x28, 0x0a,
|
||||
0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0f, 0x70, 0x72, 0x65, 0x76, 0x69, 0x6f, 0x75, 0x73,
|
||||
0x43, 0x6f, 0x75, 0x6e, 0x74, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65,
|
||||
0x72, 0x74, 0x65, 0x78, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x69, 0x70,
|
||||
0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74, 0x22, 0xd7, 0x01, 0x0a, 0x13, 0x50, 0x72, 0x65, 0x4b,
|
||||
0x65, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x6c, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12,
|
||||
0x26, 0x0a, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49,
|
||||
0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72,
|
||||
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x65, 0x4b, 0x65,
|
||||
0x79, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x72, 0x65, 0x4b, 0x65,
|
||||
0x79, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x73, 0x69, 0x67, 0x6e, 0x65, 0x64, 0x50, 0x72, 0x65,
|
||||
0x4b, 0x65, 0x79, 0x49, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0e, 0x73, 0x69, 0x67,
|
||||
0x6e, 0x65, 0x64, 0x50, 0x72, 0x65, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x62,
|
||||
0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x62, 0x61,
|
||||
0x73, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74,
|
||||
0x79, 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x69, 0x64, 0x65, 0x6e,
|
||||
0x74, 0x69, 0x74, 0x79, 0x4b, 0x65, 0x79, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61,
|
||||
0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67,
|
||||
0x65, 0x22, 0xac, 0x01, 0x0a, 0x12, 0x4b, 0x65, 0x79, 0x45, 0x78, 0x63, 0x68, 0x61, 0x6e, 0x67,
|
||||
0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01,
|
||||
0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x69, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x62, 0x61, 0x73, 0x65,
|
||||
0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x62, 0x61, 0x73, 0x65, 0x4b,
|
||||
0x65, 0x79, 0x12, 0x1e, 0x0a, 0x0a, 0x72, 0x61, 0x74, 0x63, 0x68, 0x65, 0x74, 0x4b, 0x65, 0x79,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x72, 0x61, 0x74, 0x63, 0x68, 0x65, 0x74, 0x4b,
|
||||
0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x79, 0x4b, 0x65,
|
||||
0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x74,
|
||||
0x79, 0x4b, 0x65, 0x79, 0x12, 0x2a, 0x0a, 0x10, 0x62, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x53,
|
||||
0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10,
|
||||
0x62, 0x61, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x53, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65,
|
||||
0x22, 0x60, 0x0a, 0x10, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x4b, 0x65, 0x79, 0x4d, 0x65, 0x73,
|
||||
0x73, 0x61, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d,
|
||||
0x52, 0x02, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69,
|
||||
0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65, 0x78, 0x74,
|
||||
0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x0a, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x74, 0x65,
|
||||
0x78, 0x74, 0x22, 0x88, 0x01, 0x0a, 0x1c, 0x53, 0x65, 0x6e, 0x64, 0x65, 0x72, 0x4b, 0x65, 0x79,
|
||||
0x44, 0x69, 0x73, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x73, 0x73,
|
||||
0x61, 0x67, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52,
|
||||
0x02, 0x69, 0x64, 0x12, 0x1c, 0x0a, 0x09, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e,
|
||||
0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20,
|
||||
0x01, 0x28, 0x0c, 0x52, 0x08, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a,
|
||||
0x0a, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x4b, 0x65, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28,
|
||||
0x0c, 0x52, 0x0a, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x4b, 0x65, 0x79, 0x22, 0x5c, 0x0a,
|
||||
0x1c, 0x44, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6f, 0x6e, 0x73, 0x69, 0x73, 0x74, 0x65, 0x6e,
|
||||
0x63, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1e, 0x0a,
|
||||
0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x0d, 0x52, 0x0a, 0x67, 0x65, 0x6e, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a,
|
||||
0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c,
|
||||
0x52, 0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65,
|
||||
}
|
||||
|
||||
var (
|
||||
file_serialize_WhisperTextProtocol_proto_rawDescOnce sync.Once
|
||||
file_serialize_WhisperTextProtocol_proto_rawDescData = file_serialize_WhisperTextProtocol_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_serialize_WhisperTextProtocol_proto_rawDescGZIP() []byte {
|
||||
file_serialize_WhisperTextProtocol_proto_rawDescOnce.Do(func() {
|
||||
file_serialize_WhisperTextProtocol_proto_rawDescData = protoimpl.X.CompressGZIP(file_serialize_WhisperTextProtocol_proto_rawDescData)
|
||||
})
|
||||
return file_serialize_WhisperTextProtocol_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_serialize_WhisperTextProtocol_proto_msgTypes = make([]protoimpl.MessageInfo, 6)
|
||||
var file_serialize_WhisperTextProtocol_proto_goTypes = []interface{}{
|
||||
(*SignalMessage)(nil), // 0: textsecure.SignalMessage
|
||||
(*PreKeySignalMessage)(nil), // 1: textsecure.PreKeySignalMessage
|
||||
(*KeyExchangeMessage)(nil), // 2: textsecure.KeyExchangeMessage
|
||||
(*SenderKeyMessage)(nil), // 3: textsecure.SenderKeyMessage
|
||||
(*SenderKeyDistributionMessage)(nil), // 4: textsecure.SenderKeyDistributionMessage
|
||||
(*DeviceConsistencyCodeMessage)(nil), // 5: textsecure.DeviceConsistencyCodeMessage
|
||||
}
|
||||
var file_serialize_WhisperTextProtocol_proto_depIdxs = []int32{
|
||||
0, // [0:0] is the sub-list for method output_type
|
||||
0, // [0:0] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_serialize_WhisperTextProtocol_proto_init() }
|
||||
func file_serialize_WhisperTextProtocol_proto_init() {
|
||||
if File_serialize_WhisperTextProtocol_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_serialize_WhisperTextProtocol_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SignalMessage); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_serialize_WhisperTextProtocol_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*PreKeySignalMessage); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_serialize_WhisperTextProtocol_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*KeyExchangeMessage); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_serialize_WhisperTextProtocol_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SenderKeyMessage); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_serialize_WhisperTextProtocol_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*SenderKeyDistributionMessage); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_serialize_WhisperTextProtocol_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*DeviceConsistencyCodeMessage); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_serialize_WhisperTextProtocol_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 6,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
GoTypes: file_serialize_WhisperTextProtocol_proto_goTypes,
|
||||
DependencyIndexes: file_serialize_WhisperTextProtocol_proto_depIdxs,
|
||||
MessageInfos: file_serialize_WhisperTextProtocol_proto_msgTypes,
|
||||
}.Build()
|
||||
File_serialize_WhisperTextProtocol_proto = out.File
|
||||
file_serialize_WhisperTextProtocol_proto_rawDesc = nil
|
||||
file_serialize_WhisperTextProtocol_proto_goTypes = nil
|
||||
file_serialize_WhisperTextProtocol_proto_depIdxs = nil
|
||||
}
|
||||
45
vendor/go.mau.fi/libsignal/serialize/WhisperTextProtocol.proto
vendored
Normal file
45
vendor/go.mau.fi/libsignal/serialize/WhisperTextProtocol.proto
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
// From https://github.com/signalapp/libsignal-protocol-c/blob/master/protobuf/WhisperTextProtocol.proto
|
||||
syntax = "proto2";
|
||||
package textsecure;
|
||||
|
||||
message SignalMessage {
|
||||
optional bytes ratchetKey = 1;
|
||||
optional uint32 counter = 2;
|
||||
optional uint32 previousCounter = 3;
|
||||
optional bytes ciphertext = 4;
|
||||
}
|
||||
|
||||
message PreKeySignalMessage {
|
||||
optional uint32 registrationId = 5;
|
||||
optional uint32 preKeyId = 1;
|
||||
optional uint32 signedPreKeyId = 6;
|
||||
optional bytes baseKey = 2;
|
||||
optional bytes identityKey = 3;
|
||||
optional bytes message = 4; // SignalMessage
|
||||
}
|
||||
|
||||
message KeyExchangeMessage {
|
||||
optional uint32 id = 1;
|
||||
optional bytes baseKey = 2;
|
||||
optional bytes ratchetKey = 3;
|
||||
optional bytes identityKey = 4;
|
||||
optional bytes baseKeySignature = 5;
|
||||
}
|
||||
|
||||
message SenderKeyMessage {
|
||||
optional uint32 id = 1;
|
||||
optional uint32 iteration = 2;
|
||||
optional bytes ciphertext = 3;
|
||||
}
|
||||
|
||||
message SenderKeyDistributionMessage {
|
||||
optional uint32 id = 1;
|
||||
optional uint32 iteration = 2;
|
||||
optional bytes chainKey = 3;
|
||||
optional bytes signingKey = 4;
|
||||
}
|
||||
|
||||
message DeviceConsistencyCodeMessage {
|
||||
optional uint32 generation = 1;
|
||||
optional bytes signature = 2;
|
||||
}
|
||||
272
vendor/go.mau.fi/libsignal/session/Session.go
vendored
Normal file
272
vendor/go.mau.fi/libsignal/session/Session.go
vendored
Normal file
@@ -0,0 +1,272 @@
|
||||
// Package session provides the methods necessary to build sessions
|
||||
package session
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/keys/prekey"
|
||||
"go.mau.fi/libsignal/logger"
|
||||
"go.mau.fi/libsignal/protocol"
|
||||
"go.mau.fi/libsignal/ratchet"
|
||||
"go.mau.fi/libsignal/serialize"
|
||||
"go.mau.fi/libsignal/signalerror"
|
||||
"go.mau.fi/libsignal/state/record"
|
||||
"go.mau.fi/libsignal/state/store"
|
||||
"go.mau.fi/libsignal/util/medium"
|
||||
"go.mau.fi/libsignal/util/optional"
|
||||
)
|
||||
|
||||
// NewBuilder constructs a session builder.
|
||||
func NewBuilder(sessionStore store.Session, preKeyStore store.PreKey,
|
||||
signedStore store.SignedPreKey, identityStore store.IdentityKey,
|
||||
remoteAddress *protocol.SignalAddress, serializer *serialize.Serializer) *Builder {
|
||||
|
||||
builder := Builder{
|
||||
sessionStore: sessionStore,
|
||||
preKeyStore: preKeyStore,
|
||||
signedPreKeyStore: signedStore,
|
||||
identityKeyStore: identityStore,
|
||||
remoteAddress: remoteAddress,
|
||||
serializer: serializer,
|
||||
}
|
||||
|
||||
return &builder
|
||||
}
|
||||
|
||||
// NewBuilderFromSignal Store constructs a session builder using a
|
||||
// SignalProtocol Store.
|
||||
func NewBuilderFromSignal(signalStore store.SignalProtocol,
|
||||
remoteAddress *protocol.SignalAddress, serializer *serialize.Serializer) *Builder {
|
||||
|
||||
builder := Builder{
|
||||
sessionStore: signalStore,
|
||||
preKeyStore: signalStore,
|
||||
signedPreKeyStore: signalStore,
|
||||
identityKeyStore: signalStore,
|
||||
remoteAddress: remoteAddress,
|
||||
serializer: serializer,
|
||||
}
|
||||
|
||||
return &builder
|
||||
}
|
||||
|
||||
// Builder is responsible for setting up encrypted sessions.
|
||||
// Once a session has been established, SessionCipher can be
|
||||
// used to encrypt/decrypt messages in that session.
|
||||
//
|
||||
// Sessions are built from one of three different vectors:
|
||||
// * PreKeyBundle retrieved from a server.
|
||||
// * PreKeySignalMessage received from a client.
|
||||
// * KeyExchangeMessage sent to or received from a client.
|
||||
//
|
||||
// Sessions are constructed per recipientId + deviceId tuple.
|
||||
// Remote logical users are identified by their recipientId,
|
||||
// and each logical recipientId can have multiple physical
|
||||
// devices.
|
||||
type Builder struct {
|
||||
sessionStore store.Session
|
||||
preKeyStore store.PreKey
|
||||
signedPreKeyStore store.SignedPreKey
|
||||
identityKeyStore store.IdentityKey
|
||||
remoteAddress *protocol.SignalAddress
|
||||
serializer *serialize.Serializer
|
||||
}
|
||||
|
||||
// Process builds a new session from a session record and pre
|
||||
// key signal message.
|
||||
func (b *Builder) Process(sessionRecord *record.Session, message *protocol.PreKeySignalMessage) (unsignedPreKeyID *optional.Uint32, err error) {
|
||||
|
||||
// Check to see if the keys are trusted.
|
||||
theirIdentityKey := message.IdentityKey()
|
||||
if !(b.identityKeyStore.IsTrustedIdentity(b.remoteAddress, theirIdentityKey)) {
|
||||
return nil, signalerror.ErrUntrustedIdentity
|
||||
}
|
||||
|
||||
// Use version 3 of the signal/axolotl protocol.
|
||||
unsignedPreKeyID, err = b.processV3(sessionRecord, message)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Save the identity key to our identity store.
|
||||
b.identityKeyStore.SaveIdentity(b.remoteAddress, theirIdentityKey)
|
||||
|
||||
// Return the unsignedPreKeyID
|
||||
return unsignedPreKeyID, nil
|
||||
}
|
||||
|
||||
// ProcessV3 builds a new session from a session record and pre key
|
||||
// signal message. After a session is constructed in this way, the embedded
|
||||
// SignalMessage can be decrypted.
|
||||
func (b *Builder) processV3(sessionRecord *record.Session,
|
||||
message *protocol.PreKeySignalMessage) (unsignedPreKeyID *optional.Uint32, err error) {
|
||||
|
||||
logger.Debug("Processing message with PreKeyID: ", message.PreKeyID())
|
||||
// Check to see if we've already set up a session for this V3 message.
|
||||
sessionExists := sessionRecord.HasSessionState(
|
||||
message.MessageVersion(),
|
||||
message.BaseKey().Serialize(),
|
||||
)
|
||||
if sessionExists {
|
||||
logger.Debug("We've already setup a session for this V3 message, letting bundled message fall through...")
|
||||
return optional.NewEmptyUint32(), nil
|
||||
}
|
||||
|
||||
// Load our signed prekey from our signed prekey store.
|
||||
ourSignedPreKeyRecord := b.signedPreKeyStore.LoadSignedPreKey(message.SignedPreKeyID())
|
||||
if ourSignedPreKeyRecord == nil {
|
||||
return nil, fmt.Errorf("%w with ID %d", signalerror.ErrNoSignedPreKey, message.SignedPreKeyID())
|
||||
}
|
||||
ourSignedPreKey := ourSignedPreKeyRecord.KeyPair()
|
||||
|
||||
// Build the parameters of the session.
|
||||
parameters := ratchet.NewEmptyReceiverParameters()
|
||||
parameters.SetTheirBaseKey(message.BaseKey())
|
||||
parameters.SetTheirIdentityKey(message.IdentityKey())
|
||||
parameters.SetOurIdentityKeyPair(b.identityKeyStore.GetIdentityKeyPair())
|
||||
parameters.SetOurSignedPreKey(ourSignedPreKey)
|
||||
parameters.SetOurRatchetKey(ourSignedPreKey)
|
||||
|
||||
// Set our one time pre key with the one from our prekey store
|
||||
// if the message contains a valid pre key id
|
||||
if !message.PreKeyID().IsEmpty {
|
||||
oneTimePreKey := b.preKeyStore.LoadPreKey(message.PreKeyID().Value)
|
||||
if oneTimePreKey == nil {
|
||||
return nil, fmt.Errorf("%w with ID %d", signalerror.ErrNoOneTimeKeyFound, message.PreKeyID().Value)
|
||||
}
|
||||
parameters.SetOurOneTimePreKey(oneTimePreKey.KeyPair())
|
||||
} else {
|
||||
parameters.SetOurOneTimePreKey(nil)
|
||||
}
|
||||
|
||||
// If this is a fresh record, archive our current state.
|
||||
if !sessionRecord.IsFresh() {
|
||||
sessionRecord.ArchiveCurrentState()
|
||||
}
|
||||
|
||||
///////// Initialize our session /////////
|
||||
sessionState := sessionRecord.SessionState()
|
||||
derivedKeys, sessionErr := ratchet.CalculateReceiverSession(parameters)
|
||||
if sessionErr != nil {
|
||||
return nil, sessionErr
|
||||
}
|
||||
sessionState.SetVersion(protocol.CurrentVersion)
|
||||
sessionState.SetRemoteIdentityKey(parameters.TheirIdentityKey())
|
||||
sessionState.SetLocalIdentityKey(parameters.OurIdentityKeyPair().PublicKey())
|
||||
sessionState.SetSenderChain(parameters.OurRatchetKey(), derivedKeys.ChainKey)
|
||||
sessionState.SetRootKey(derivedKeys.RootKey)
|
||||
|
||||
// Set the session's registration ids and base key
|
||||
sessionState.SetLocalRegistrationID(b.identityKeyStore.GetLocalRegistrationId())
|
||||
sessionState.SetRemoteRegistrationID(message.RegistrationID())
|
||||
sessionState.SetSenderBaseKey(message.BaseKey().Serialize())
|
||||
|
||||
// Remove the PreKey from our store and return the message prekey id if it is valid.
|
||||
if message.PreKeyID() != nil && message.PreKeyID().Value != medium.MaxValue {
|
||||
return message.PreKeyID(), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ProcessBundle builds a new session from a PreKeyBundle retrieved
|
||||
// from a server.
|
||||
func (b *Builder) ProcessBundle(preKey *prekey.Bundle) error {
|
||||
// Check to see if the keys are trusted.
|
||||
if !(b.identityKeyStore.IsTrustedIdentity(b.remoteAddress, preKey.IdentityKey())) {
|
||||
return signalerror.ErrUntrustedIdentity
|
||||
}
|
||||
|
||||
// Check to see if the bundle has a signed pre key.
|
||||
if preKey.SignedPreKey() == nil {
|
||||
return signalerror.ErrNoSignedPreKey
|
||||
}
|
||||
|
||||
// Verify the signature of the pre key
|
||||
preKeyPublic := preKey.IdentityKey().PublicKey()
|
||||
preKeyBytes := preKey.SignedPreKey().Serialize()
|
||||
preKeySignature := preKey.SignedPreKeySignature()
|
||||
if !ecc.VerifySignature(preKeyPublic, preKeyBytes, preKeySignature) {
|
||||
return signalerror.ErrInvalidSignature
|
||||
}
|
||||
|
||||
// Load our session and generate keys.
|
||||
sessionRecord := b.sessionStore.LoadSession(b.remoteAddress)
|
||||
ourBaseKey, err := ecc.GenerateKeyPair()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
theirSignedPreKey := preKey.SignedPreKey()
|
||||
theirOneTimePreKey := preKey.PreKey()
|
||||
theirOneTimePreKeyID := preKey.PreKeyID()
|
||||
|
||||
// Build the parameters of the session
|
||||
parameters := ratchet.NewEmptySenderParameters()
|
||||
parameters.SetOurBaseKey(ourBaseKey)
|
||||
parameters.SetOurIdentityKey(b.identityKeyStore.GetIdentityKeyPair())
|
||||
parameters.SetTheirIdentityKey(preKey.IdentityKey())
|
||||
parameters.SetTheirSignedPreKey(theirSignedPreKey)
|
||||
parameters.SetTheirRatchetKey(theirSignedPreKey)
|
||||
parameters.SetTheirOneTimePreKey(theirOneTimePreKey)
|
||||
|
||||
// If this is a fresh record, archive our current state.
|
||||
if !sessionRecord.IsFresh() {
|
||||
sessionRecord.ArchiveCurrentState()
|
||||
}
|
||||
|
||||
///////// Initialize our session /////////
|
||||
sessionState := sessionRecord.SessionState()
|
||||
derivedKeys, sessionErr := ratchet.CalculateSenderSession(parameters)
|
||||
if sessionErr != nil {
|
||||
return sessionErr
|
||||
}
|
||||
// Generate an ephemeral "ratchet" key that will be advertised to
|
||||
// the receiving user.
|
||||
sendingRatchetKey, keyErr := ecc.GenerateKeyPair()
|
||||
if keyErr != nil {
|
||||
return keyErr
|
||||
}
|
||||
sendingChain, chainErr := derivedKeys.RootKey.CreateChain(
|
||||
parameters.TheirRatchetKey(),
|
||||
sendingRatchetKey,
|
||||
)
|
||||
if chainErr != nil {
|
||||
return chainErr
|
||||
}
|
||||
|
||||
// Calculate the sender session.
|
||||
sessionState.SetVersion(protocol.CurrentVersion)
|
||||
sessionState.SetRemoteIdentityKey(parameters.TheirIdentityKey())
|
||||
sessionState.SetLocalIdentityKey(parameters.OurIdentityKey().PublicKey())
|
||||
sessionState.AddReceiverChain(parameters.TheirRatchetKey(), derivedKeys.ChainKey.Current())
|
||||
sessionState.SetSenderChain(sendingRatchetKey, sendingChain.ChainKey)
|
||||
sessionState.SetRootKey(sendingChain.RootKey)
|
||||
|
||||
// Update our session record with the unackowledged prekey message
|
||||
sessionState.SetUnacknowledgedPreKeyMessage(
|
||||
theirOneTimePreKeyID,
|
||||
preKey.SignedPreKeyID(),
|
||||
ourBaseKey.PublicKey(),
|
||||
)
|
||||
|
||||
// Set the local registration ID based on the registration id in our identity key store.
|
||||
sessionState.SetLocalRegistrationID(
|
||||
b.identityKeyStore.GetLocalRegistrationId(),
|
||||
)
|
||||
|
||||
// Set the remote registration ID based on the given prekey bundle registrationID.
|
||||
sessionState.SetRemoteRegistrationID(
|
||||
preKey.RegistrationID(),
|
||||
)
|
||||
|
||||
// Set the sender base key in our session record state.
|
||||
sessionState.SetSenderBaseKey(
|
||||
ourBaseKey.PublicKey().Serialize(),
|
||||
)
|
||||
|
||||
// Store the session in our session store and save the identity in our identity store.
|
||||
b.sessionStore.StoreSession(b.remoteAddress, sessionRecord)
|
||||
b.identityKeyStore.SaveIdentity(b.remoteAddress, preKey.IdentityKey())
|
||||
|
||||
return nil
|
||||
}
|
||||
366
vendor/go.mau.fi/libsignal/session/SessionCipher.go
vendored
Normal file
366
vendor/go.mau.fi/libsignal/session/SessionCipher.go
vendored
Normal file
@@ -0,0 +1,366 @@
|
||||
package session
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"go.mau.fi/libsignal/cipher"
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/keys/chain"
|
||||
"go.mau.fi/libsignal/keys/message"
|
||||
"go.mau.fi/libsignal/logger"
|
||||
"go.mau.fi/libsignal/protocol"
|
||||
"go.mau.fi/libsignal/signalerror"
|
||||
"go.mau.fi/libsignal/state/record"
|
||||
"go.mau.fi/libsignal/state/store"
|
||||
"go.mau.fi/libsignal/util/bytehelper"
|
||||
)
|
||||
|
||||
const maxFutureMessages = 2000
|
||||
|
||||
// NewCipher constructs a session cipher for encrypt/decrypt operations on a
|
||||
// session. In order to use the session cipher, a session must have already
|
||||
// been created and stored using session.Builder.
|
||||
func NewCipher(builder *Builder, remoteAddress *protocol.SignalAddress) *Cipher {
|
||||
cipher := &Cipher{
|
||||
sessionStore: builder.sessionStore,
|
||||
preKeyMessageSerializer: builder.serializer.PreKeySignalMessage,
|
||||
signalMessageSerializer: builder.serializer.SignalMessage,
|
||||
preKeyStore: builder.preKeyStore,
|
||||
remoteAddress: remoteAddress,
|
||||
builder: builder,
|
||||
identityKeyStore: builder.identityKeyStore,
|
||||
}
|
||||
|
||||
return cipher
|
||||
}
|
||||
|
||||
func NewCipherFromSession(remoteAddress *protocol.SignalAddress,
|
||||
sessionStore store.Session, preKeyStore store.PreKey, identityKeyStore store.IdentityKey,
|
||||
preKeyMessageSerializer protocol.PreKeySignalMessageSerializer,
|
||||
signalMessageSerializer protocol.SignalMessageSerializer) *Cipher {
|
||||
cipher := &Cipher{
|
||||
sessionStore: sessionStore,
|
||||
preKeyMessageSerializer: preKeyMessageSerializer,
|
||||
signalMessageSerializer: signalMessageSerializer,
|
||||
preKeyStore: preKeyStore,
|
||||
remoteAddress: remoteAddress,
|
||||
identityKeyStore: identityKeyStore,
|
||||
}
|
||||
|
||||
return cipher
|
||||
}
|
||||
|
||||
// Cipher is the main entry point for Signal Protocol encrypt/decrypt operations.
|
||||
// Once a session has been established with session.Builder, this can be used for
|
||||
// all encrypt/decrypt operations within that session.
|
||||
type Cipher struct {
|
||||
sessionStore store.Session
|
||||
preKeyMessageSerializer protocol.PreKeySignalMessageSerializer
|
||||
signalMessageSerializer protocol.SignalMessageSerializer
|
||||
preKeyStore store.PreKey
|
||||
remoteAddress *protocol.SignalAddress
|
||||
builder *Builder
|
||||
identityKeyStore store.IdentityKey
|
||||
}
|
||||
|
||||
// Encrypt will take the given message in bytes and return an object that follows
|
||||
// the CiphertextMessage interface.
|
||||
func (d *Cipher) Encrypt(plaintext []byte) (protocol.CiphertextMessage, error) {
|
||||
sessionRecord := d.sessionStore.LoadSession(d.remoteAddress)
|
||||
sessionState := sessionRecord.SessionState()
|
||||
chainKey := sessionState.SenderChainKey()
|
||||
messageKeys := chainKey.MessageKeys()
|
||||
senderEphemeral := sessionState.SenderRatchetKey()
|
||||
previousCounter := sessionState.PreviousCounter()
|
||||
sessionVersion := sessionState.Version()
|
||||
|
||||
ciphertextBody, err := encrypt(messageKeys, plaintext)
|
||||
logger.Debug("Got ciphertextBody: ", ciphertextBody)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var ciphertextMessage protocol.CiphertextMessage
|
||||
ciphertextMessage, err = protocol.NewSignalMessage(
|
||||
sessionVersion,
|
||||
chainKey.Index(),
|
||||
previousCounter,
|
||||
messageKeys.MacKey(),
|
||||
senderEphemeral,
|
||||
ciphertextBody,
|
||||
sessionState.LocalIdentityKey(),
|
||||
sessionState.RemoteIdentityKey(),
|
||||
d.signalMessageSerializer,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// If we haven't established a session with the recipient yet,
|
||||
// send our message as a PreKeySignalMessage.
|
||||
if sessionState.HasUnacknowledgedPreKeyMessage() {
|
||||
items, err := sessionState.UnackPreKeyMessageItems()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
localRegistrationID := sessionState.LocalRegistrationID()
|
||||
|
||||
ciphertextMessage, err = protocol.NewPreKeySignalMessage(
|
||||
sessionVersion,
|
||||
localRegistrationID,
|
||||
items.PreKeyID(),
|
||||
items.SignedPreKeyID(),
|
||||
items.BaseKey(),
|
||||
sessionState.LocalIdentityKey(),
|
||||
ciphertextMessage.(*protocol.SignalMessage),
|
||||
d.preKeyMessageSerializer,
|
||||
d.signalMessageSerializer,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
sessionState.SetSenderChainKey(chainKey.NextKey())
|
||||
if !d.identityKeyStore.IsTrustedIdentity(d.remoteAddress, sessionState.RemoteIdentityKey()) {
|
||||
// return err
|
||||
}
|
||||
d.identityKeyStore.SaveIdentity(d.remoteAddress, sessionState.RemoteIdentityKey())
|
||||
d.sessionStore.StoreSession(d.remoteAddress, sessionRecord)
|
||||
return ciphertextMessage, nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts the given message using an existing session that
|
||||
// is stored in the session store.
|
||||
func (d *Cipher) Decrypt(ciphertextMessage *protocol.SignalMessage) ([]byte, error) {
|
||||
plaintext, _, err := d.DecryptAndGetKey(ciphertextMessage)
|
||||
|
||||
return plaintext, err
|
||||
}
|
||||
|
||||
// DecryptAndGetKey decrypts the given message using an existing session that
|
||||
// is stored in the session store and returns the message keys used for encryption.
|
||||
func (d *Cipher) DecryptAndGetKey(ciphertextMessage *protocol.SignalMessage) ([]byte, *message.Keys, error) {
|
||||
if !d.sessionStore.ContainsSession(d.remoteAddress) {
|
||||
return nil, nil, fmt.Errorf("%w %s", signalerror.ErrNoSessionForUser, d.remoteAddress.String())
|
||||
}
|
||||
|
||||
// Load the session record from our session store and decrypt the message.
|
||||
sessionRecord := d.sessionStore.LoadSession(d.remoteAddress)
|
||||
plaintext, messageKeys, err := d.DecryptWithRecord(sessionRecord, ciphertextMessage)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if !d.identityKeyStore.IsTrustedIdentity(d.remoteAddress, sessionRecord.SessionState().RemoteIdentityKey()) {
|
||||
// return err
|
||||
}
|
||||
d.identityKeyStore.SaveIdentity(d.remoteAddress, sessionRecord.SessionState().RemoteIdentityKey())
|
||||
|
||||
// Store the session record in our session store.
|
||||
d.sessionStore.StoreSession(d.remoteAddress, sessionRecord)
|
||||
return plaintext, messageKeys, nil
|
||||
}
|
||||
|
||||
func (d *Cipher) DecryptMessage(ciphertextMessage *protocol.PreKeySignalMessage) ([]byte, error) {
|
||||
plaintext, _, err := d.DecryptMessageReturnKey(ciphertextMessage)
|
||||
return plaintext, err
|
||||
}
|
||||
|
||||
func (d *Cipher) DecryptMessageReturnKey(ciphertextMessage *protocol.PreKeySignalMessage) ([]byte, *message.Keys, error) {
|
||||
// Load or create session record for this session.
|
||||
sessionRecord := d.sessionStore.LoadSession(d.remoteAddress)
|
||||
unsignedPreKeyID, err := d.builder.Process(sessionRecord, ciphertextMessage)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
plaintext, keys, err := d.DecryptWithRecord(sessionRecord, ciphertextMessage.WhisperMessage())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
// Store the session record in our session store.
|
||||
d.sessionStore.StoreSession(d.remoteAddress, sessionRecord)
|
||||
if !unsignedPreKeyID.IsEmpty {
|
||||
d.preKeyStore.RemovePreKey(unsignedPreKeyID.Value)
|
||||
}
|
||||
return plaintext, keys, nil
|
||||
}
|
||||
|
||||
// DecryptWithKey will decrypt the given message using the given symmetric key. This
|
||||
// can be used when decrypting messages at a later time if the message key was saved.
|
||||
func (d *Cipher) DecryptWithKey(ciphertextMessage *protocol.SignalMessage, key *message.Keys) ([]byte, error) {
|
||||
logger.Debug("Decrypting ciphertext body: ", ciphertextMessage.Body())
|
||||
plaintext, err := decrypt(key, ciphertextMessage.Body())
|
||||
if err != nil {
|
||||
logger.Error("Unable to get plain text from ciphertext: ", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
// DecryptWithRecord decrypts the given message using the given session record.
|
||||
func (d *Cipher) DecryptWithRecord(sessionRecord *record.Session, ciphertext *protocol.SignalMessage) ([]byte, *message.Keys, error) {
|
||||
logger.Debug("Decrypting ciphertext with record: ", sessionRecord)
|
||||
previousStates := sessionRecord.PreviousSessionStates()
|
||||
sessionState := sessionRecord.SessionState()
|
||||
|
||||
// Try and decrypt the message with the current session state.
|
||||
plaintext, messageKeys, err := d.DecryptWithState(sessionState, ciphertext)
|
||||
|
||||
// If we received an error using the current session state, loop
|
||||
// through all previous states.
|
||||
if err != nil {
|
||||
logger.Warning(err)
|
||||
for i, state := range previousStates {
|
||||
// Try decrypting the message with previous states
|
||||
plaintext, messageKeys, err = d.DecryptWithState(state, ciphertext)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// If successful, remove and promote the state.
|
||||
previousStates = append(previousStates[:i], previousStates[i+1:]...)
|
||||
sessionRecord.PromoteState(state)
|
||||
|
||||
return plaintext, messageKeys, nil
|
||||
}
|
||||
|
||||
return nil, nil, signalerror.ErrNoValidSessions
|
||||
}
|
||||
|
||||
// If decryption was successful, set the session state and return the plain text.
|
||||
sessionRecord.SetState(sessionState)
|
||||
|
||||
return plaintext, messageKeys, nil
|
||||
}
|
||||
|
||||
// DecryptWithState decrypts the given message with the given session state.
|
||||
func (d *Cipher) DecryptWithState(sessionState *record.State, ciphertextMessage *protocol.SignalMessage) ([]byte, *message.Keys, error) {
|
||||
logger.Debug("Decrypting ciphertext with session state: ", sessionState)
|
||||
if !sessionState.HasSenderChain() {
|
||||
logger.Error("Unable to decrypt message with state: ", signalerror.ErrUninitializedSession)
|
||||
return nil, nil, signalerror.ErrUninitializedSession
|
||||
}
|
||||
|
||||
if ciphertextMessage.MessageVersion() != sessionState.Version() {
|
||||
logger.Error("Unable to decrypt message with state: ", signalerror.ErrWrongMessageVersion)
|
||||
return nil, nil, signalerror.ErrWrongMessageVersion
|
||||
}
|
||||
|
||||
messageVersion := ciphertextMessage.MessageVersion()
|
||||
theirEphemeral := ciphertextMessage.SenderRatchetKey()
|
||||
counter := ciphertextMessage.Counter()
|
||||
chainKey, chainCreateErr := getOrCreateChainKey(sessionState, theirEphemeral)
|
||||
if chainCreateErr != nil {
|
||||
logger.Error("Unable to get or create chain key: ", chainCreateErr)
|
||||
return nil, nil, fmt.Errorf("failed to get or create chain key: %w", chainCreateErr)
|
||||
}
|
||||
|
||||
messageKeys, keysCreateErr := getOrCreateMessageKeys(sessionState, theirEphemeral, chainKey, counter)
|
||||
if keysCreateErr != nil {
|
||||
logger.Error("Unable to get or create message keys: ", keysCreateErr)
|
||||
return nil, nil, fmt.Errorf("failed to get or create message keys: %w", keysCreateErr)
|
||||
}
|
||||
|
||||
err := ciphertextMessage.VerifyMac(messageVersion, sessionState.RemoteIdentityKey(), sessionState.LocalIdentityKey(), messageKeys.MacKey())
|
||||
if err != nil {
|
||||
logger.Error("Unable to verify ciphertext mac: ", err)
|
||||
return nil, nil, fmt.Errorf("failed to verify ciphertext MAC: %w", err)
|
||||
}
|
||||
|
||||
plaintext, err := d.DecryptWithKey(ciphertextMessage, messageKeys)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
sessionState.ClearUnackPreKeyMessage()
|
||||
|
||||
return plaintext, messageKeys, nil
|
||||
}
|
||||
|
||||
func getOrCreateMessageKeys(sessionState *record.State, theirEphemeral ecc.ECPublicKeyable,
|
||||
chainKey *chain.Key, counter uint32) (*message.Keys, error) {
|
||||
|
||||
if chainKey.Index() > counter {
|
||||
if sessionState.HasMessageKeys(theirEphemeral, counter) {
|
||||
return sessionState.RemoveMessageKeys(theirEphemeral, counter), nil
|
||||
}
|
||||
return nil, fmt.Errorf("%w (index: %d, count: %d)", signalerror.ErrOldCounter, chainKey.Index(), counter)
|
||||
}
|
||||
|
||||
if counter-chainKey.Index() > maxFutureMessages {
|
||||
return nil, signalerror.ErrTooFarIntoFuture
|
||||
}
|
||||
|
||||
for chainKey.Index() < counter {
|
||||
messageKeys := chainKey.MessageKeys()
|
||||
sessionState.SetMessageKeys(theirEphemeral, messageKeys)
|
||||
chainKey = chainKey.NextKey()
|
||||
}
|
||||
|
||||
sessionState.SetReceiverChainKey(theirEphemeral, chainKey.NextKey())
|
||||
return chainKey.MessageKeys(), nil
|
||||
}
|
||||
|
||||
// getOrCreateChainKey will either return the existing chain key or
|
||||
// create a new one with the given session state and ephemeral key.
|
||||
func getOrCreateChainKey(sessionState *record.State, theirEphemeral ecc.ECPublicKeyable) (*chain.Key, error) {
|
||||
|
||||
// If our session state already has a receiver chain, use their
|
||||
// ephemeral key in the existing chain.
|
||||
if sessionState.HasReceiverChain(theirEphemeral) {
|
||||
return sessionState.ReceiverChainKey(theirEphemeral), nil
|
||||
}
|
||||
|
||||
// If we don't have a chain key, create one with ephemeral keys.
|
||||
rootKey := sessionState.RootKey()
|
||||
ourEphemeral := sessionState.SenderRatchetKeyPair()
|
||||
receiverChain, rErr := rootKey.CreateChain(theirEphemeral, ourEphemeral)
|
||||
if rErr != nil {
|
||||
return nil, rErr
|
||||
}
|
||||
|
||||
// Generate a new ephemeral key pair.
|
||||
ourNewEphemeral, gErr := ecc.GenerateKeyPair()
|
||||
if gErr != nil {
|
||||
return nil, gErr
|
||||
}
|
||||
|
||||
// Create a new chain using our new ephemeral key.
|
||||
senderChain, cErr := receiverChain.RootKey.CreateChain(theirEphemeral, ourNewEphemeral)
|
||||
if cErr != nil {
|
||||
return nil, cErr
|
||||
}
|
||||
|
||||
// Set our session state parameters.
|
||||
sessionState.SetRootKey(senderChain.RootKey)
|
||||
sessionState.AddReceiverChain(theirEphemeral, receiverChain.ChainKey)
|
||||
previousCounter := max(sessionState.SenderChainKey().Index()-1, 0)
|
||||
sessionState.SetPreviousCounter(previousCounter)
|
||||
sessionState.SetSenderChain(ourNewEphemeral, senderChain.ChainKey)
|
||||
|
||||
return receiverChain.ChainKey.(*chain.Key), nil
|
||||
}
|
||||
|
||||
// decrypt will use the given message keys and ciphertext and return
|
||||
// the plaintext bytes.
|
||||
func decrypt(keys *message.Keys, body []byte) ([]byte, error) {
|
||||
logger.Debug("Using cipherKey: ", keys.CipherKey())
|
||||
return cipher.DecryptCbc(keys.Iv(), keys.CipherKey(), bytehelper.CopySlice(body))
|
||||
}
|
||||
|
||||
// encrypt will use the given cipher, message keys, and plaintext bytes
|
||||
// and return ciphertext bytes.
|
||||
func encrypt(messageKeys *message.Keys, plaintext []byte) ([]byte, error) {
|
||||
logger.Debug("Using cipherKey: ", messageKeys.CipherKey())
|
||||
return cipher.EncryptCbc(messageKeys.Iv(), messageKeys.CipherKey(), plaintext)
|
||||
}
|
||||
|
||||
// Max is a uint32 implementation of math.Max
|
||||
func max(x, y uint32) uint32 {
|
||||
if x > y {
|
||||
return x
|
||||
}
|
||||
return y
|
||||
}
|
||||
37
vendor/go.mau.fi/libsignal/signalerror/errors.go
vendored
Normal file
37
vendor/go.mau.fi/libsignal/signalerror/errors.go
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
package signalerror
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrNoSenderKeyStatesInRecord = errors.New("no sender key states in record")
|
||||
ErrNoSenderKeyStateForID = errors.New("no sender key state for key ID")
|
||||
)
|
||||
|
||||
var (
|
||||
ErrUntrustedIdentity = errors.New("untrusted identity")
|
||||
ErrNoSignedPreKey = errors.New("no signed prekey found in bundle")
|
||||
ErrInvalidSignature = errors.New("invalid signature on device key")
|
||||
ErrNoOneTimeKeyFound = errors.New("prekey store didn't return one-time key")
|
||||
)
|
||||
|
||||
var (
|
||||
ErrNoValidSessions = errors.New("no valid sessions")
|
||||
ErrUninitializedSession = errors.New("uninitialized session")
|
||||
ErrWrongMessageVersion = errors.New("wrong message version")
|
||||
ErrTooFarIntoFuture = errors.New("message index is over 2000 messages into the future")
|
||||
ErrOldCounter = errors.New("received message with old counter")
|
||||
ErrNoSessionForUser = errors.New("no session found for user")
|
||||
)
|
||||
|
||||
var (
|
||||
ErrSenderKeyStateVerificationFailed = errors.New("sender key state failed verification with given public key")
|
||||
ErrNoSenderKeyForUser = errors.New("no sender key")
|
||||
)
|
||||
|
||||
var (
|
||||
ErrOldMessageVersion = errors.New("too old message version")
|
||||
ErrUnknownMessageVersion = errors.New("unknown message version")
|
||||
ErrIncompleteMessage = errors.New("incomplete message")
|
||||
)
|
||||
|
||||
var ErrBadMAC = errors.New("mismatching MAC in signal message")
|
||||
157
vendor/go.mau.fi/libsignal/state/record/ChainState.go
vendored
Normal file
157
vendor/go.mau.fi/libsignal/state/record/ChainState.go
vendored
Normal file
@@ -0,0 +1,157 @@
|
||||
package record
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/kdf"
|
||||
"go.mau.fi/libsignal/keys/chain"
|
||||
"go.mau.fi/libsignal/keys/message"
|
||||
"go.mau.fi/libsignal/util/bytehelper"
|
||||
)
|
||||
|
||||
// NewReceiverChainPair will return a new ReceiverChainPair object.
|
||||
func NewReceiverChainPair(receiverChain *Chain, index int) *ReceiverChainPair {
|
||||
return &ReceiverChainPair{
|
||||
ReceiverChain: receiverChain,
|
||||
Index: index,
|
||||
}
|
||||
}
|
||||
|
||||
// ReceiverChainPair is a structure for a receiver chain key and index number.
|
||||
type ReceiverChainPair struct {
|
||||
ReceiverChain *Chain
|
||||
Index int
|
||||
}
|
||||
|
||||
// NewChain returns a new Chain structure for SessionState.
|
||||
func NewChain(senderRatchetKeyPair *ecc.ECKeyPair, chainKey *chain.Key,
|
||||
messageKeys []*message.Keys) *Chain {
|
||||
|
||||
return &Chain{
|
||||
senderRatchetKeyPair: senderRatchetKeyPair,
|
||||
chainKey: chainKey,
|
||||
messageKeys: messageKeys,
|
||||
}
|
||||
}
|
||||
|
||||
// NewChainFromStructure will return a new Chain with the given
|
||||
// chain structure.
|
||||
func NewChainFromStructure(structure *ChainStructure) (*Chain, error) {
|
||||
// Alias to SliceToArray
|
||||
getArray := bytehelper.SliceToArray
|
||||
|
||||
// Build the sender ratchet key from bytes.
|
||||
senderRatchetKeyPublic, err := ecc.DecodePoint(structure.SenderRatchetKeyPublic, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var senderRatchetKeyPrivate ecc.ECPrivateKeyable
|
||||
if len(structure.SenderRatchetKeyPrivate) == 32 {
|
||||
senderRatchetKeyPrivate = ecc.NewDjbECPrivateKey(getArray(structure.SenderRatchetKeyPrivate))
|
||||
}
|
||||
senderRatchetKeyPair := ecc.NewECKeyPair(senderRatchetKeyPublic, senderRatchetKeyPrivate)
|
||||
|
||||
// Build our message keys from the message key structures.
|
||||
messageKeys := make([]*message.Keys, len(structure.MessageKeys))
|
||||
for i := range structure.MessageKeys {
|
||||
messageKeys[i] = message.NewKeysFromStruct(structure.MessageKeys[i])
|
||||
}
|
||||
|
||||
// Build our new chain state.
|
||||
chainState := NewChain(
|
||||
senderRatchetKeyPair,
|
||||
chain.NewKeyFromStruct(structure.ChainKey, kdf.DeriveSecrets),
|
||||
messageKeys,
|
||||
)
|
||||
|
||||
return chainState, nil
|
||||
}
|
||||
|
||||
// ChainStructure is a serializeable structure for chain states.
|
||||
type ChainStructure struct {
|
||||
SenderRatchetKeyPublic []byte
|
||||
SenderRatchetKeyPrivate []byte
|
||||
ChainKey *chain.KeyStructure
|
||||
MessageKeys []*message.KeysStructure
|
||||
}
|
||||
|
||||
// Chain is a structure used inside the SessionState that keeps
|
||||
// track of an ongoing ratcheting chain for a session.
|
||||
type Chain struct {
|
||||
senderRatchetKeyPair *ecc.ECKeyPair
|
||||
chainKey *chain.Key
|
||||
messageKeys []*message.Keys
|
||||
}
|
||||
|
||||
// SenderRatchetKey returns the sender's EC keypair.
|
||||
func (c *Chain) SenderRatchetKey() *ecc.ECKeyPair {
|
||||
return c.senderRatchetKeyPair
|
||||
}
|
||||
|
||||
// SetSenderRatchetKey will set the chain state with the given EC
|
||||
// key pair.
|
||||
func (c *Chain) SetSenderRatchetKey(key *ecc.ECKeyPair) {
|
||||
c.senderRatchetKeyPair = key
|
||||
}
|
||||
|
||||
// ChainKey will return the chain key in the chain state.
|
||||
func (c *Chain) ChainKey() *chain.Key {
|
||||
return c.chainKey
|
||||
}
|
||||
|
||||
// SetChainKey will set the chain state's chain key.
|
||||
func (c *Chain) SetChainKey(key *chain.Key) {
|
||||
c.chainKey = key
|
||||
}
|
||||
|
||||
// MessageKeys will return the message keys associated with the
|
||||
// chain state.
|
||||
func (c *Chain) MessageKeys() []*message.Keys {
|
||||
return c.messageKeys
|
||||
}
|
||||
|
||||
// SetMessageKeys will set the chain state with the given message
|
||||
// keys.
|
||||
func (c *Chain) SetMessageKeys(keys []*message.Keys) {
|
||||
c.messageKeys = keys
|
||||
}
|
||||
|
||||
// AddMessageKeys will append the chain state with the given
|
||||
// message keys.
|
||||
func (c *Chain) AddMessageKeys(keys *message.Keys) {
|
||||
c.messageKeys = append(c.messageKeys, keys)
|
||||
}
|
||||
|
||||
// PopFirstMessageKeys will remove the first message key from
|
||||
// the chain's list of message keys.
|
||||
func (c *Chain) PopFirstMessageKeys() *message.Keys {
|
||||
removed := c.messageKeys[0]
|
||||
c.messageKeys = c.messageKeys[1:]
|
||||
|
||||
return removed
|
||||
}
|
||||
|
||||
// structure returns a serializeable structure of the chain state.
|
||||
func (c *Chain) structure() *ChainStructure {
|
||||
// Alias to ArrayToSlice
|
||||
getSlice := bytehelper.ArrayToSlice
|
||||
|
||||
// Convert our message keys into a serializeable structure.
|
||||
messageKeys := make([]*message.KeysStructure, len(c.messageKeys))
|
||||
for i := range c.messageKeys {
|
||||
messageKeys[i] = message.NewStructFromKeys(c.messageKeys[i])
|
||||
}
|
||||
|
||||
// Convert our sender ratchet key private
|
||||
var senderRatchetKeyPrivate []byte
|
||||
if c.senderRatchetKeyPair.PrivateKey() != nil {
|
||||
senderRatchetKeyPrivate = getSlice(c.senderRatchetKeyPair.PrivateKey().Serialize())
|
||||
}
|
||||
|
||||
// Build the chain structure.
|
||||
return &ChainStructure{
|
||||
SenderRatchetKeyPublic: c.senderRatchetKeyPair.PublicKey().Serialize(),
|
||||
SenderRatchetKeyPrivate: senderRatchetKeyPrivate,
|
||||
ChainKey: chain.NewStructFromKey(c.chainKey),
|
||||
MessageKeys: messageKeys,
|
||||
}
|
||||
}
|
||||
3
vendor/go.mau.fi/libsignal/state/record/Doc.go
vendored
Normal file
3
vendor/go.mau.fi/libsignal/state/record/Doc.go
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package record provides the state and record of an ongoing double
|
||||
// ratchet session.
|
||||
package record
|
||||
91
vendor/go.mau.fi/libsignal/state/record/PendingKeyExchangeState.go
vendored
Normal file
91
vendor/go.mau.fi/libsignal/state/record/PendingKeyExchangeState.go
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
package record
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/keys/identity"
|
||||
"go.mau.fi/libsignal/util/bytehelper"
|
||||
)
|
||||
|
||||
// NewPendingKeyExchange will return a new PendingKeyExchange object.
|
||||
func NewPendingKeyExchange(sequence uint32, localBaseKeyPair, localRatchetKeyPair *ecc.ECKeyPair,
|
||||
localIdentityKeyPair *identity.KeyPair) *PendingKeyExchange {
|
||||
|
||||
return &PendingKeyExchange{
|
||||
sequence: sequence,
|
||||
localBaseKeyPair: localBaseKeyPair,
|
||||
localRatchetKeyPair: localRatchetKeyPair,
|
||||
localIdentityKeyPair: localIdentityKeyPair,
|
||||
}
|
||||
}
|
||||
|
||||
// NewPendingKeyExchangeFromStruct will return a PendingKeyExchange object from
|
||||
// the given structure. This is used to get a deserialized pending prekey exchange
|
||||
// fetched from persistent storage.
|
||||
func NewPendingKeyExchangeFromStruct(structure *PendingKeyExchangeStructure) *PendingKeyExchange {
|
||||
// Return nil if no structure was provided.
|
||||
if structure == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Alias the SliceToArray method.
|
||||
getArray := bytehelper.SliceToArray
|
||||
|
||||
// Convert the bytes in the given structure to ECC objects.
|
||||
localBaseKeyPair := ecc.NewECKeyPair(
|
||||
ecc.NewDjbECPublicKey(getArray(structure.LocalBaseKeyPublic)),
|
||||
ecc.NewDjbECPrivateKey(getArray(structure.LocalBaseKeyPrivate)),
|
||||
)
|
||||
localRatchetKeyPair := ecc.NewECKeyPair(
|
||||
ecc.NewDjbECPublicKey(getArray(structure.LocalRatchetKeyPublic)),
|
||||
ecc.NewDjbECPrivateKey(getArray(structure.LocalRatchetKeyPrivate)),
|
||||
)
|
||||
localIdentityKeyPair := identity.NewKeyPair(
|
||||
identity.NewKey(ecc.NewDjbECPublicKey(getArray(structure.LocalIdentityKeyPublic))),
|
||||
ecc.NewDjbECPrivateKey(getArray(structure.LocalIdentityKeyPrivate)),
|
||||
)
|
||||
|
||||
// Return the PendingKeyExchange with the deserialized keys.
|
||||
return &PendingKeyExchange{
|
||||
sequence: structure.Sequence,
|
||||
localBaseKeyPair: localBaseKeyPair,
|
||||
localRatchetKeyPair: localRatchetKeyPair,
|
||||
localIdentityKeyPair: localIdentityKeyPair,
|
||||
}
|
||||
}
|
||||
|
||||
// PendingKeyExchangeStructure is a serializable structure for pending
|
||||
// key exchanges. This structure is used for persistent storage of the
|
||||
// key exchange state.
|
||||
type PendingKeyExchangeStructure struct {
|
||||
Sequence uint32
|
||||
LocalBaseKeyPublic []byte
|
||||
LocalBaseKeyPrivate []byte
|
||||
LocalRatchetKeyPublic []byte
|
||||
LocalRatchetKeyPrivate []byte
|
||||
LocalIdentityKeyPublic []byte
|
||||
LocalIdentityKeyPrivate []byte
|
||||
}
|
||||
|
||||
// PendingKeyExchange is a structure for storing a pending
|
||||
// key exchange for a session state.
|
||||
type PendingKeyExchange struct {
|
||||
sequence uint32
|
||||
localBaseKeyPair *ecc.ECKeyPair
|
||||
localRatchetKeyPair *ecc.ECKeyPair
|
||||
localIdentityKeyPair *identity.KeyPair
|
||||
}
|
||||
|
||||
// structre will return a serializable structure of a pending key exchange
|
||||
// so it can be persistently stored.
|
||||
func (p *PendingKeyExchange) structure() *PendingKeyExchangeStructure {
|
||||
getSlice := bytehelper.ArrayToSlice
|
||||
return &PendingKeyExchangeStructure{
|
||||
Sequence: p.sequence,
|
||||
LocalBaseKeyPublic: getSlice(p.localBaseKeyPair.PublicKey().PublicKey()),
|
||||
LocalBaseKeyPrivate: getSlice(p.localBaseKeyPair.PrivateKey().Serialize()),
|
||||
LocalRatchetKeyPublic: getSlice(p.localRatchetKeyPair.PublicKey().PublicKey()),
|
||||
LocalRatchetKeyPrivate: getSlice(p.localRatchetKeyPair.PrivateKey().Serialize()),
|
||||
LocalIdentityKeyPublic: getSlice(p.localIdentityKeyPair.PublicKey().PublicKey().PublicKey()),
|
||||
LocalIdentityKeyPrivate: getSlice(p.localIdentityKeyPair.PrivateKey().Serialize()),
|
||||
}
|
||||
}
|
||||
62
vendor/go.mau.fi/libsignal/state/record/PendingPreKeyState.go
vendored
Normal file
62
vendor/go.mau.fi/libsignal/state/record/PendingPreKeyState.go
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
package record
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/util/optional"
|
||||
)
|
||||
|
||||
// NewPendingPreKey will return a new pending pre key object.
|
||||
func NewPendingPreKey(preKeyID *optional.Uint32, signedPreKeyID uint32,
|
||||
baseKey ecc.ECPublicKeyable) *PendingPreKey {
|
||||
|
||||
return &PendingPreKey{
|
||||
preKeyID: preKeyID,
|
||||
signedPreKeyID: signedPreKeyID,
|
||||
baseKey: baseKey,
|
||||
}
|
||||
}
|
||||
|
||||
// NewPendingPreKeyFromStruct will return a new pending prekey object from the
|
||||
// given structure.
|
||||
func NewPendingPreKeyFromStruct(preKey *PendingPreKeyStructure) (*PendingPreKey, error) {
|
||||
baseKey, err := ecc.DecodePoint(preKey.BaseKey, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pendingPreKey := NewPendingPreKey(
|
||||
preKey.PreKeyID,
|
||||
preKey.SignedPreKeyID,
|
||||
baseKey,
|
||||
)
|
||||
|
||||
return pendingPreKey, nil
|
||||
}
|
||||
|
||||
// PendingPreKeyStructure is a serializeable structure for pending
|
||||
// prekeys.
|
||||
type PendingPreKeyStructure struct {
|
||||
PreKeyID *optional.Uint32
|
||||
SignedPreKeyID uint32
|
||||
BaseKey []byte
|
||||
}
|
||||
|
||||
// PendingPreKey is a structure for pending pre keys
|
||||
// for a session state.
|
||||
type PendingPreKey struct {
|
||||
preKeyID *optional.Uint32
|
||||
signedPreKeyID uint32
|
||||
baseKey ecc.ECPublicKeyable
|
||||
}
|
||||
|
||||
// structure will return a serializeable structure of the pending prekey.
|
||||
func (p *PendingPreKey) structure() *PendingPreKeyStructure {
|
||||
if p != nil {
|
||||
return &PendingPreKeyStructure{
|
||||
PreKeyID: p.preKeyID,
|
||||
SignedPreKeyID: p.signedPreKeyID,
|
||||
BaseKey: p.baseKey.Serialize(),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
90
vendor/go.mau.fi/libsignal/state/record/PreKeyRecord.go
vendored
Normal file
90
vendor/go.mau.fi/libsignal/state/record/PreKeyRecord.go
vendored
Normal file
@@ -0,0 +1,90 @@
|
||||
package record
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/util/bytehelper"
|
||||
"go.mau.fi/libsignal/util/optional"
|
||||
)
|
||||
|
||||
// PreKeySerializer is an interface for serializing and deserializing
|
||||
// PreKey objects into bytes. An implementation of this interface should be
|
||||
// used to encode/decode the object into JSON, Protobuffers, etc.
|
||||
type PreKeySerializer interface {
|
||||
Serialize(preKey *PreKeyStructure) []byte
|
||||
Deserialize(serialized []byte) (*PreKeyStructure, error)
|
||||
}
|
||||
|
||||
// NewPreKeyFromBytes will return a prekey record from the given bytes using the given serializer.
|
||||
func NewPreKeyFromBytes(serialized []byte, serializer PreKeySerializer) (*PreKey, error) {
|
||||
// Use the given serializer to decode the signal message.
|
||||
preKeyStructure, err := serializer.Deserialize(serialized)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewPreKeyFromStruct(preKeyStructure, serializer)
|
||||
}
|
||||
|
||||
// NewPreKeyFromStruct returns a PreKey record using the given serializable structure.
|
||||
func NewPreKeyFromStruct(structure *PreKeyStructure, serializer PreKeySerializer) (*PreKey, error) {
|
||||
// Create the prekey record from the structure.
|
||||
preKey := &PreKey{
|
||||
structure: *structure,
|
||||
serializer: serializer,
|
||||
}
|
||||
|
||||
// Generate the ECC key from bytes.
|
||||
publicKey := ecc.NewDjbECPublicKey(bytehelper.SliceToArray(structure.PublicKey))
|
||||
privateKey := ecc.NewDjbECPrivateKey(bytehelper.SliceToArray(structure.PrivateKey))
|
||||
keyPair := ecc.NewECKeyPair(publicKey, privateKey)
|
||||
preKey.keyPair = keyPair
|
||||
|
||||
return preKey, nil
|
||||
}
|
||||
|
||||
// NewPreKey record returns a new pre key record that can
|
||||
// be stored in a PreKeyStore.
|
||||
func NewPreKey(id uint32, keyPair *ecc.ECKeyPair, serializer PreKeySerializer) *PreKey {
|
||||
return &PreKey{
|
||||
structure: PreKeyStructure{
|
||||
ID: id,
|
||||
PublicKey: keyPair.PublicKey().Serialize(),
|
||||
PrivateKey: bytehelper.ArrayToSlice(keyPair.PrivateKey().Serialize()),
|
||||
},
|
||||
keyPair: keyPair,
|
||||
serializer: serializer,
|
||||
}
|
||||
}
|
||||
|
||||
// PreKeyStructure is a structure for serializing PreKey records.
|
||||
type PreKeyStructure struct {
|
||||
ID uint32
|
||||
PublicKey []byte
|
||||
PrivateKey []byte
|
||||
}
|
||||
|
||||
// PreKey record is a structure for storing pre keys inside
|
||||
// a PreKeyStore.
|
||||
type PreKey struct {
|
||||
structure PreKeyStructure
|
||||
keyPair *ecc.ECKeyPair
|
||||
serializer PreKeySerializer
|
||||
}
|
||||
|
||||
// ID returns the pre key record's id.
|
||||
func (p *PreKey) ID() *optional.Uint32 {
|
||||
// TODO: manually set this to empty if empty
|
||||
return optional.NewOptionalUint32(p.structure.ID)
|
||||
}
|
||||
|
||||
// KeyPair returns the pre key record's key pair.
|
||||
func (p *PreKey) KeyPair() *ecc.ECKeyPair {
|
||||
return p.keyPair
|
||||
}
|
||||
|
||||
// Serialize uses the PreKey serializer to return the PreKey
|
||||
// as serialized bytes.
|
||||
func (p *PreKey) Serialize() []byte {
|
||||
structure := p.structure
|
||||
return p.serializer.Serialize(&structure)
|
||||
}
|
||||
197
vendor/go.mau.fi/libsignal/state/record/SessionRecord.go
vendored
Normal file
197
vendor/go.mau.fi/libsignal/state/record/SessionRecord.go
vendored
Normal file
@@ -0,0 +1,197 @@
|
||||
package record
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
)
|
||||
|
||||
// archivedStatesMaxLength describes how many previous session
|
||||
// states we should keep track of.
|
||||
const archivedStatesMaxLength int = 40
|
||||
|
||||
// SessionSerializer is an interface for serializing and deserializing
|
||||
// a Signal Session into bytes. An implementation of this interface should be
|
||||
// used to encode/decode the object into JSON, Protobuffers, etc.
|
||||
type SessionSerializer interface {
|
||||
Serialize(state *SessionStructure) []byte
|
||||
Deserialize(serialized []byte) (*SessionStructure, error)
|
||||
}
|
||||
|
||||
// NewSessionFromBytes will return a Signal Session from the given
|
||||
// bytes using the given serializer.
|
||||
func NewSessionFromBytes(serialized []byte, serializer SessionSerializer, stateSerializer StateSerializer) (*Session, error) {
|
||||
// Use the given serializer to decode the session.
|
||||
sessionStructure, err := serializer.Deserialize(serialized)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewSessionFromStructure(sessionStructure, serializer, stateSerializer)
|
||||
}
|
||||
|
||||
// NewSession creates a new session record and uses the given session and state
|
||||
// serializers to convert the object into storeable bytes.
|
||||
func NewSession(serializer SessionSerializer, stateSerializer StateSerializer) *Session {
|
||||
record := Session{
|
||||
sessionState: NewState(stateSerializer),
|
||||
previousStates: []*State{},
|
||||
fresh: true,
|
||||
serializer: serializer,
|
||||
}
|
||||
|
||||
return &record
|
||||
}
|
||||
|
||||
// NewSessionFromStructure will return a new Signal Session from the given
|
||||
// session structure and serializer.
|
||||
func NewSessionFromStructure(structure *SessionStructure, serializer SessionSerializer,
|
||||
stateSerializer StateSerializer) (*Session, error) {
|
||||
|
||||
// Build our previous states from structure.
|
||||
previousStates := make([]*State, len(structure.PreviousStates))
|
||||
for i := range structure.PreviousStates {
|
||||
var err error
|
||||
previousStates[i], err = NewStateFromStructure(structure.PreviousStates[i], stateSerializer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Build our current state from structure.
|
||||
sessionState, err := NewStateFromStructure(structure.SessionState, stateSerializer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Build and return our session.
|
||||
session := &Session{
|
||||
previousStates: previousStates,
|
||||
sessionState: sessionState,
|
||||
serializer: serializer,
|
||||
fresh: false,
|
||||
}
|
||||
|
||||
return session, nil
|
||||
}
|
||||
|
||||
// NewSessionFromState creates a new session record from the given
|
||||
// session state.
|
||||
func NewSessionFromState(sessionState *State, serializer SessionSerializer) *Session {
|
||||
record := Session{
|
||||
sessionState: sessionState,
|
||||
previousStates: []*State{},
|
||||
fresh: false,
|
||||
serializer: serializer,
|
||||
}
|
||||
|
||||
return &record
|
||||
}
|
||||
|
||||
// SessionStructure is a public, serializeable structure for Signal
|
||||
// Sessions. The states defined in the session are immuteable, as
|
||||
// they should not be changed by anyone but the serializer.
|
||||
type SessionStructure struct {
|
||||
SessionState *StateStructure
|
||||
PreviousStates []*StateStructure
|
||||
}
|
||||
|
||||
// Session encapsulates the state of an ongoing session.
|
||||
type Session struct {
|
||||
serializer SessionSerializer
|
||||
sessionState *State
|
||||
previousStates []*State
|
||||
fresh bool
|
||||
}
|
||||
|
||||
// SetState sets the session record's current state to the given
|
||||
// one.
|
||||
func (r *Session) SetState(sessionState *State) {
|
||||
r.sessionState = sessionState
|
||||
}
|
||||
|
||||
// IsFresh is used to determine if this is a brand new session
|
||||
// or if a session record has already existed.
|
||||
func (r *Session) IsFresh() bool {
|
||||
return r.fresh
|
||||
}
|
||||
|
||||
// SessionState returns the session state object of the current
|
||||
// session record.
|
||||
func (r *Session) SessionState() *State {
|
||||
return r.sessionState
|
||||
}
|
||||
|
||||
// PreviousSessionStates returns a list of all currently maintained
|
||||
// "previous" session states.
|
||||
func (r *Session) PreviousSessionStates() []*State {
|
||||
return r.previousStates
|
||||
}
|
||||
|
||||
// HasSessionState will check this record to see if the sender's
|
||||
// base key exists in the current and previous states.
|
||||
func (r *Session) HasSessionState(version int, senderBaseKey []byte) bool {
|
||||
// Ensure the session state version is identical to this one.
|
||||
if r.sessionState.Version() == version && (bytes.Compare(senderBaseKey, r.sessionState.SenderBaseKey()) == 0) {
|
||||
return true
|
||||
}
|
||||
|
||||
// Loop through all of our previous states and see if this
|
||||
// exists in our state.
|
||||
for i := range r.previousStates {
|
||||
if r.previousStates[i].Version() == version && bytes.Compare(senderBaseKey, r.previousStates[i].SenderBaseKey()) == 0 {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// ArchiveCurrentState moves the current session state into the list
|
||||
// of "previous" session states, and replaces the current session state
|
||||
// with a fresh reset instance.
|
||||
func (r *Session) ArchiveCurrentState() {
|
||||
r.PromoteState(NewState(r.sessionState.serializer))
|
||||
}
|
||||
|
||||
// PromoteState takes the given session state and replaces it with the
|
||||
// current state, pushing the previous current state to "previousStates".
|
||||
func (r *Session) PromoteState(promotedState *State) {
|
||||
r.previousStates = r.prependStates(r.previousStates, r.sessionState)
|
||||
r.sessionState = promotedState
|
||||
|
||||
// Remove the last state if it has reached our maximum length
|
||||
if len(r.previousStates) > archivedStatesMaxLength {
|
||||
r.previousStates = r.removeLastState(r.previousStates)
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize will return the session as serialized bytes so it can be
|
||||
// persistently stored.
|
||||
func (r *Session) Serialize() []byte {
|
||||
return r.serializer.Serialize(r.Structure())
|
||||
}
|
||||
|
||||
// prependStates takes an array/slice of states and prepends it with
|
||||
// the given session state.
|
||||
func (r *Session) prependStates(states []*State, sessionState *State) []*State {
|
||||
return append([]*State{sessionState}, states...)
|
||||
}
|
||||
|
||||
// removeLastState takes an array/slice of states and removes the
|
||||
// last element from it.
|
||||
func (r *Session) removeLastState(states []*State) []*State {
|
||||
return states[:len(states)-1]
|
||||
}
|
||||
|
||||
// Structure will return a simple serializable session structure
|
||||
// from the given structure. This is used for serialization to persistently
|
||||
// store a session record.
|
||||
func (r *Session) Structure() *SessionStructure {
|
||||
previousStates := make([]*StateStructure, len(r.previousStates))
|
||||
for i := range r.previousStates {
|
||||
previousStates[i] = r.previousStates[i].structure()
|
||||
}
|
||||
return &SessionStructure{
|
||||
SessionState: r.sessionState.structure(),
|
||||
PreviousStates: previousStates,
|
||||
}
|
||||
}
|
||||
531
vendor/go.mau.fi/libsignal/state/record/SessionState.go
vendored
Normal file
531
vendor/go.mau.fi/libsignal/state/record/SessionState.go
vendored
Normal file
@@ -0,0 +1,531 @@
|
||||
package record
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/kdf"
|
||||
"go.mau.fi/libsignal/keys/chain"
|
||||
"go.mau.fi/libsignal/keys/identity"
|
||||
"go.mau.fi/libsignal/keys/message"
|
||||
"go.mau.fi/libsignal/keys/root"
|
||||
"go.mau.fi/libsignal/keys/session"
|
||||
"go.mau.fi/libsignal/logger"
|
||||
"go.mau.fi/libsignal/util/errorhelper"
|
||||
"go.mau.fi/libsignal/util/optional"
|
||||
)
|
||||
|
||||
const maxMessageKeys int = 2000
|
||||
const maxReceiverChains int = 5
|
||||
|
||||
// StateSerializer is an interface for serializing and deserializing
|
||||
// a Signal State into bytes. An implementation of this interface should be
|
||||
// used to encode/decode the object into JSON, Protobuffers, etc.
|
||||
type StateSerializer interface {
|
||||
Serialize(state *StateStructure) []byte
|
||||
Deserialize(serialized []byte) (*StateStructure, error)
|
||||
}
|
||||
|
||||
// NewStateFromBytes will return a Signal State from the given
|
||||
// bytes using the given serializer.
|
||||
func NewStateFromBytes(serialized []byte, serializer StateSerializer) (*State, error) {
|
||||
// Use the given serializer to decode the signal message.
|
||||
stateStructure, err := serializer.Deserialize(serialized)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewStateFromStructure(stateStructure, serializer)
|
||||
}
|
||||
|
||||
// NewState returns a new session state.
|
||||
func NewState(serializer StateSerializer) *State {
|
||||
return &State{serializer: serializer}
|
||||
}
|
||||
|
||||
// NewStateFromStructure will return a new session state with the
|
||||
// given state structure.
|
||||
func NewStateFromStructure(structure *StateStructure, serializer StateSerializer) (*State, error) {
|
||||
// Keep a list of errors, so they can be handled once.
|
||||
errors := errorhelper.NewMultiError()
|
||||
|
||||
// Convert our ecc keys from bytes into object form.
|
||||
localIdentityPublic, err := ecc.DecodePoint(structure.LocalIdentityPublic, 0)
|
||||
errors.Add(err)
|
||||
remoteIdentityPublic, err := ecc.DecodePoint(structure.RemoteIdentityPublic, 0)
|
||||
errors.Add(err)
|
||||
senderBaseKey, err := ecc.DecodePoint(structure.SenderBaseKey, 0)
|
||||
errors.Add(err)
|
||||
var pendingPreKey *PendingPreKey
|
||||
if structure.PendingPreKey != nil {
|
||||
pendingPreKey, err = NewPendingPreKeyFromStruct(structure.PendingPreKey)
|
||||
errors.Add(err)
|
||||
}
|
||||
senderChain, err := NewChainFromStructure(structure.SenderChain)
|
||||
errors.Add(err)
|
||||
|
||||
// Build our receiver chains from structure.
|
||||
receiverChains := make([]*Chain, len(structure.ReceiverChains))
|
||||
for i := range structure.ReceiverChains {
|
||||
receiverChains[i], err = NewChainFromStructure(structure.ReceiverChains[i])
|
||||
errors.Add(err)
|
||||
}
|
||||
|
||||
// Handle any errors. The first error will always be returned if there are multiple.
|
||||
if errors.HasErrors() {
|
||||
return nil, errors
|
||||
}
|
||||
|
||||
// Build our state object.
|
||||
state := &State{
|
||||
localIdentityPublic: identity.NewKey(localIdentityPublic),
|
||||
localRegistrationID: structure.LocalRegistrationID,
|
||||
needsRefresh: structure.NeedsRefresh,
|
||||
pendingKeyExchange: NewPendingKeyExchangeFromStruct(structure.PendingKeyExchange),
|
||||
pendingPreKey: pendingPreKey,
|
||||
previousCounter: structure.PreviousCounter,
|
||||
receiverChains: receiverChains,
|
||||
remoteIdentityPublic: identity.NewKey(remoteIdentityPublic),
|
||||
remoteRegistrationID: structure.RemoteRegistrationID,
|
||||
rootKey: root.NewKey(kdf.DeriveSecrets, structure.RootKey),
|
||||
senderBaseKey: senderBaseKey,
|
||||
senderChain: senderChain,
|
||||
serializer: serializer,
|
||||
sessionVersion: structure.SessionVersion,
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// StateStructure is the structure of a session state. Fields are public
|
||||
// to be used for serialization and deserialization.
|
||||
type StateStructure struct {
|
||||
LocalIdentityPublic []byte
|
||||
LocalRegistrationID uint32
|
||||
NeedsRefresh bool
|
||||
PendingKeyExchange *PendingKeyExchangeStructure
|
||||
PendingPreKey *PendingPreKeyStructure
|
||||
PreviousCounter uint32
|
||||
ReceiverChains []*ChainStructure
|
||||
RemoteIdentityPublic []byte
|
||||
RemoteRegistrationID uint32
|
||||
RootKey []byte
|
||||
SenderBaseKey []byte
|
||||
SenderChain *ChainStructure
|
||||
SessionVersion int
|
||||
}
|
||||
|
||||
// State is a session state that contains the structure for
|
||||
// all sessions. Session states are contained inside session records.
|
||||
// The session state is implemented as a struct rather than protobuffers
|
||||
// to allow other serialization methods.
|
||||
type State struct {
|
||||
localIdentityPublic *identity.Key
|
||||
localRegistrationID uint32
|
||||
needsRefresh bool
|
||||
pendingKeyExchange *PendingKeyExchange
|
||||
pendingPreKey *PendingPreKey
|
||||
previousCounter uint32
|
||||
receiverChains []*Chain
|
||||
remoteIdentityPublic *identity.Key
|
||||
remoteRegistrationID uint32
|
||||
rootKey *root.Key
|
||||
senderBaseKey ecc.ECPublicKeyable
|
||||
senderChain *Chain
|
||||
serializer StateSerializer
|
||||
sessionVersion int
|
||||
}
|
||||
|
||||
// SenderBaseKey returns the sender's base key in bytes.
|
||||
func (s *State) SenderBaseKey() []byte {
|
||||
if s.senderBaseKey == nil {
|
||||
return nil
|
||||
}
|
||||
return s.senderBaseKey.Serialize()
|
||||
}
|
||||
|
||||
// SetSenderBaseKey sets the sender's base key with the given bytes.
|
||||
func (s *State) SetSenderBaseKey(senderBaseKey []byte) {
|
||||
s.senderBaseKey, _ = ecc.DecodePoint(senderBaseKey, 0)
|
||||
}
|
||||
|
||||
// Version returns the session's version.
|
||||
func (s *State) Version() int {
|
||||
return s.sessionVersion
|
||||
}
|
||||
|
||||
// SetVersion sets the session state's version number.
|
||||
func (s *State) SetVersion(version int) {
|
||||
s.sessionVersion = version
|
||||
}
|
||||
|
||||
// RemoteIdentityKey returns the identity key of the remote user.
|
||||
func (s *State) RemoteIdentityKey() *identity.Key {
|
||||
return s.remoteIdentityPublic
|
||||
}
|
||||
|
||||
// SetRemoteIdentityKey sets this session's identity key for the remote
|
||||
// user.
|
||||
func (s *State) SetRemoteIdentityKey(identityKey *identity.Key) {
|
||||
s.remoteIdentityPublic = identityKey
|
||||
}
|
||||
|
||||
// LocalIdentityKey returns the session's identity key for the local
|
||||
// user.
|
||||
func (s *State) LocalIdentityKey() *identity.Key {
|
||||
return s.localIdentityPublic
|
||||
}
|
||||
|
||||
// SetLocalIdentityKey sets the session's identity key for the local
|
||||
// user.
|
||||
func (s *State) SetLocalIdentityKey(identityKey *identity.Key) {
|
||||
s.localIdentityPublic = identityKey
|
||||
}
|
||||
|
||||
// PreviousCounter returns the counter of the previous message.
|
||||
func (s *State) PreviousCounter() uint32 {
|
||||
return s.previousCounter
|
||||
}
|
||||
|
||||
// SetPreviousCounter sets the counter for the previous message.
|
||||
func (s *State) SetPreviousCounter(previousCounter uint32) {
|
||||
s.previousCounter = previousCounter
|
||||
}
|
||||
|
||||
// RootKey returns the root key for the session.
|
||||
func (s *State) RootKey() session.RootKeyable {
|
||||
return s.rootKey
|
||||
}
|
||||
|
||||
// SetRootKey sets the root key for the session.
|
||||
func (s *State) SetRootKey(rootKey session.RootKeyable) {
|
||||
s.rootKey = rootKey.(*root.Key)
|
||||
}
|
||||
|
||||
// SenderRatchetKey returns the public ratchet key of the sender.
|
||||
func (s *State) SenderRatchetKey() ecc.ECPublicKeyable {
|
||||
return s.senderChain.senderRatchetKeyPair.PublicKey()
|
||||
}
|
||||
|
||||
// SenderRatchetKeyPair returns the public/private ratchet key pair
|
||||
// of the sender.
|
||||
func (s *State) SenderRatchetKeyPair() *ecc.ECKeyPair {
|
||||
return s.senderChain.senderRatchetKeyPair
|
||||
}
|
||||
|
||||
// HasReceiverChain will check to see if the session state has
|
||||
// the given ephemeral key.
|
||||
func (s *State) HasReceiverChain(senderEphemeral ecc.ECPublicKeyable) bool {
|
||||
return s.receiverChain(senderEphemeral) != nil
|
||||
}
|
||||
|
||||
// HasSenderChain will check to see if the session state has a
|
||||
// sender chain.
|
||||
func (s *State) HasSenderChain() bool {
|
||||
return s.senderChain != nil
|
||||
}
|
||||
|
||||
// receiverChain will loop through the session state's receiver chains
|
||||
// and compare the given ephemeral key. If it is found, then the chain
|
||||
// and index will be returned as a pair.
|
||||
func (s *State) receiverChain(senderEphemeral ecc.ECPublicKeyable) *ReceiverChainPair {
|
||||
receiverChains := s.receiverChains
|
||||
|
||||
for i, receiverChain := range receiverChains {
|
||||
chainSenderRatchetKey, err := ecc.DecodePoint(receiverChain.senderRatchetKeyPair.PublicKey().Serialize(), 0)
|
||||
if err != nil {
|
||||
logger.Error("Error getting receiverchain: ", err)
|
||||
}
|
||||
|
||||
// If the chainSenderRatchetKey equals our senderEphemeral key, return it.
|
||||
if chainSenderRatchetKey.PublicKey() == senderEphemeral.PublicKey() {
|
||||
return NewReceiverChainPair(receiverChain, i)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// ReceiverChainKey will use the given ephemeral key to generate a new
|
||||
// chain key.
|
||||
func (s *State) ReceiverChainKey(senderEphemeral ecc.ECPublicKeyable) *chain.Key {
|
||||
receiverChainAndIndex := s.receiverChain(senderEphemeral)
|
||||
receiverChain := receiverChainAndIndex.ReceiverChain
|
||||
|
||||
if receiverChainAndIndex == nil || receiverChain == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return chain.NewKey(
|
||||
kdf.DeriveSecrets,
|
||||
receiverChain.chainKey.Key(),
|
||||
receiverChain.chainKey.Index(),
|
||||
)
|
||||
}
|
||||
|
||||
// AddReceiverChain will add the given ratchet key and chain key to the session
|
||||
// state.
|
||||
func (s *State) AddReceiverChain(senderRatchetKey ecc.ECPublicKeyable, chainKey session.ChainKeyable) {
|
||||
// Create a keypair structure with our sender ratchet key.
|
||||
senderKey := ecc.NewECKeyPair(senderRatchetKey, nil)
|
||||
|
||||
// Create a Chain state object that will hold our sender key, chain key, and
|
||||
// message keys.
|
||||
chain := NewChain(senderKey, chainKey.(*chain.Key), []*message.Keys{})
|
||||
|
||||
// Add the Chain state to our list of receiver chain states.
|
||||
s.receiverChains = append(s.receiverChains, chain)
|
||||
|
||||
// If our list of receiver chains is too big, delete the oldest entry.
|
||||
if len(s.receiverChains) > maxReceiverChains {
|
||||
i := 0
|
||||
s.receiverChains = append(s.receiverChains[:i], s.receiverChains[i+1:]...)
|
||||
}
|
||||
}
|
||||
|
||||
// SetSenderChain will set the given ratchet key pair and chain key for this session
|
||||
// state.
|
||||
func (s *State) SetSenderChain(senderRatchetKeyPair *ecc.ECKeyPair, chainKey session.ChainKeyable) {
|
||||
// Create a Chain state object that will hold our sender key, chain key, and
|
||||
// message keys.
|
||||
chain := NewChain(senderRatchetKeyPair, chainKey.(*chain.Key), []*message.Keys{})
|
||||
|
||||
// Set the sender chain.
|
||||
s.senderChain = chain
|
||||
}
|
||||
|
||||
// SenderChainKey will return the chain key of the session state.
|
||||
func (s *State) SenderChainKey() session.ChainKeyable {
|
||||
chainKey := s.senderChain.chainKey
|
||||
return chain.NewKey(kdf.DeriveSecrets, chainKey.Key(), chainKey.Index())
|
||||
}
|
||||
|
||||
// SetSenderChainKey will set the chain key in the chain state for this session to
|
||||
// the given chain key.
|
||||
func (s *State) SetSenderChainKey(nextChainKey session.ChainKeyable) {
|
||||
senderChain := s.senderChain
|
||||
senderChain.SetChainKey(nextChainKey.(*chain.Key))
|
||||
}
|
||||
|
||||
// HasMessageKeys returns true if we have message keys associated with the given
|
||||
// sender key and counter.
|
||||
func (s *State) HasMessageKeys(senderEphemeral ecc.ECPublicKeyable, counter uint32) bool {
|
||||
// Get our chain state that has our chain key.
|
||||
chainAndIndex := s.receiverChain(senderEphemeral)
|
||||
receiverChain := chainAndIndex.ReceiverChain
|
||||
|
||||
// If the chain is empty, we don't have any message keys.
|
||||
if receiverChain == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// Get our message keys from our receiver chain.
|
||||
messageKeyList := receiverChain.MessageKeys()
|
||||
|
||||
// Loop through our message keys and compare its index with the
|
||||
// given counter.
|
||||
for _, messageKey := range messageKeyList {
|
||||
if messageKey.Index() == counter {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// RemoveMessageKeys removes the message key with the given sender key and
|
||||
// counter. It will return the removed message key.
|
||||
func (s *State) RemoveMessageKeys(senderEphemeral ecc.ECPublicKeyable, counter uint32) *message.Keys {
|
||||
// Get our chain state that has our chain key.
|
||||
chainAndIndex := s.receiverChain(senderEphemeral)
|
||||
chainKey := chainAndIndex.ReceiverChain
|
||||
|
||||
// If the chain is empty, we don't have any message keys.
|
||||
if chainKey == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get our message keys from our receiver chain.
|
||||
messageKeyList := chainKey.MessageKeys()
|
||||
|
||||
// Loop through our message keys and compare its index with the
|
||||
// given counter. When we find a match, remove it from our list.
|
||||
var rmIndex int
|
||||
for i, messageKey := range messageKeyList {
|
||||
if messageKey.Index() == counter {
|
||||
rmIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Retrive the message key
|
||||
messageKey := chainKey.messageKeys[rmIndex]
|
||||
|
||||
// Delete the message key from the given position.
|
||||
chainKey.messageKeys = append(chainKey.messageKeys[:rmIndex], chainKey.messageKeys[rmIndex+1:]...)
|
||||
|
||||
return message.NewKeys(
|
||||
messageKey.CipherKey(),
|
||||
messageKey.MacKey(),
|
||||
messageKey.Iv(),
|
||||
messageKey.Index(),
|
||||
)
|
||||
}
|
||||
|
||||
// SetMessageKeys will update the chain associated with the given sender key with
|
||||
// the given message keys.
|
||||
func (s *State) SetMessageKeys(senderEphemeral ecc.ECPublicKeyable, messageKeys *message.Keys) {
|
||||
chainAndIndex := s.receiverChain(senderEphemeral)
|
||||
chainState := chainAndIndex.ReceiverChain
|
||||
|
||||
// Add the message keys to our chain state.
|
||||
chainState.AddMessageKeys(
|
||||
message.NewKeys(
|
||||
messageKeys.CipherKey(),
|
||||
messageKeys.MacKey(),
|
||||
messageKeys.Iv(),
|
||||
messageKeys.Index(),
|
||||
),
|
||||
)
|
||||
|
||||
if len(chainState.MessageKeys()) > maxMessageKeys {
|
||||
chainState.PopFirstMessageKeys()
|
||||
}
|
||||
}
|
||||
|
||||
// SetReceiverChainKey sets the session's receiver chain key with the given chain key
|
||||
// associated with the given senderEphemeral key.
|
||||
func (s *State) SetReceiverChainKey(senderEphemeral ecc.ECPublicKeyable, chainKey session.ChainKeyable) {
|
||||
chainAndIndex := s.receiverChain(senderEphemeral)
|
||||
chainState := chainAndIndex.ReceiverChain
|
||||
chainState.SetChainKey(chainKey.(*chain.Key))
|
||||
}
|
||||
|
||||
// SetPendingKeyExchange will set the session's pending key exchange state to the given
|
||||
// sequence and key pairs.
|
||||
func (s *State) SetPendingKeyExchange(sequence uint32, ourBaseKey, ourRatchetKey *ecc.ECKeyPair,
|
||||
ourIdentityKey *identity.KeyPair) {
|
||||
|
||||
s.pendingKeyExchange = NewPendingKeyExchange(
|
||||
sequence,
|
||||
ourBaseKey,
|
||||
ourRatchetKey,
|
||||
ourIdentityKey,
|
||||
)
|
||||
}
|
||||
|
||||
// PendingKeyExchangeSequence will return the session's pending key exchange sequence
|
||||
// number.
|
||||
func (s *State) PendingKeyExchangeSequence() uint32 {
|
||||
return s.pendingKeyExchange.sequence
|
||||
}
|
||||
|
||||
// PendingKeyExchangeBaseKeyPair will return the session's pending key exchange base keypair.
|
||||
func (s *State) PendingKeyExchangeBaseKeyPair() *ecc.ECKeyPair {
|
||||
return s.pendingKeyExchange.localBaseKeyPair
|
||||
}
|
||||
|
||||
// PendingKeyExchangeRatchetKeyPair will return the session's pending key exchange ratchet
|
||||
// keypair.
|
||||
func (s *State) PendingKeyExchangeRatchetKeyPair() *ecc.ECKeyPair {
|
||||
return s.pendingKeyExchange.localRatchetKeyPair
|
||||
}
|
||||
|
||||
// PendingKeyExchangeIdentityKeyPair will return the session's pending key exchange identity
|
||||
// keypair.
|
||||
func (s *State) PendingKeyExchangeIdentityKeyPair() *identity.KeyPair {
|
||||
return s.pendingKeyExchange.localIdentityKeyPair
|
||||
}
|
||||
|
||||
// HasPendingKeyExchange will return true if there is a valid pending key exchange waiting.
|
||||
func (s *State) HasPendingKeyExchange() bool {
|
||||
return s.pendingKeyExchange != nil
|
||||
}
|
||||
|
||||
// SetUnacknowledgedPreKeyMessage will return unacknowledged pre key message with the
|
||||
// given key ids and base key.
|
||||
func (s *State) SetUnacknowledgedPreKeyMessage(preKeyID *optional.Uint32, signedPreKeyID uint32, baseKey ecc.ECPublicKeyable) {
|
||||
s.pendingPreKey = NewPendingPreKey(
|
||||
preKeyID,
|
||||
signedPreKeyID,
|
||||
baseKey,
|
||||
)
|
||||
}
|
||||
|
||||
// HasUnacknowledgedPreKeyMessage will return true if this session has an unacknowledged
|
||||
// pre key message.
|
||||
func (s *State) HasUnacknowledgedPreKeyMessage() bool {
|
||||
return s.pendingPreKey != nil
|
||||
}
|
||||
|
||||
// UnackPreKeyMessageItems will return the session's unacknowledged pre key messages.
|
||||
func (s *State) UnackPreKeyMessageItems() (*UnackPreKeyMessageItems, error) {
|
||||
preKeyID := s.pendingPreKey.preKeyID
|
||||
signedPreKeyID := s.pendingPreKey.signedPreKeyID
|
||||
baseKey, err := ecc.DecodePoint(s.pendingPreKey.baseKey.Serialize(), 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return NewUnackPreKeyMessageItems(preKeyID, signedPreKeyID, baseKey), nil
|
||||
}
|
||||
|
||||
// ClearUnackPreKeyMessage will clear the session's pending pre key.
|
||||
func (s *State) ClearUnackPreKeyMessage() {
|
||||
s.pendingPreKey = nil
|
||||
}
|
||||
|
||||
// SetRemoteRegistrationID sets the remote user's registration id.
|
||||
func (s *State) SetRemoteRegistrationID(registrationID uint32) {
|
||||
s.remoteRegistrationID = registrationID
|
||||
}
|
||||
|
||||
// RemoteRegistrationID returns the remote user's registration id.
|
||||
func (s *State) RemoteRegistrationID() uint32 {
|
||||
return s.remoteRegistrationID
|
||||
}
|
||||
|
||||
// SetLocalRegistrationID sets the local user's registration id.
|
||||
func (s *State) SetLocalRegistrationID(registrationID uint32) {
|
||||
s.localRegistrationID = registrationID
|
||||
}
|
||||
|
||||
// LocalRegistrationID returns the local user's registration id.
|
||||
func (s *State) LocalRegistrationID() uint32 {
|
||||
return s.localRegistrationID
|
||||
}
|
||||
|
||||
// Serialize will return the state as bytes using the given serializer.
|
||||
func (s *State) Serialize() []byte {
|
||||
return s.serializer.Serialize(s.structure())
|
||||
}
|
||||
|
||||
// structure will return a serializable structure of the
|
||||
// the given state so it can be persistently stored.
|
||||
func (s *State) structure() *StateStructure {
|
||||
// Convert our receiver chains into a serializeable structure
|
||||
receiverChains := make([]*ChainStructure, len(s.receiverChains))
|
||||
for i := range s.receiverChains {
|
||||
receiverChains[i] = s.receiverChains[i].structure()
|
||||
}
|
||||
|
||||
// Convert our pending key exchange into a serializeable structure
|
||||
var pendingKeyExchange *PendingKeyExchangeStructure
|
||||
if s.pendingKeyExchange != nil {
|
||||
pendingKeyExchange = s.pendingKeyExchange.structure()
|
||||
}
|
||||
|
||||
// Build and return our state structure.
|
||||
return &StateStructure{
|
||||
LocalIdentityPublic: s.localIdentityPublic.Serialize(),
|
||||
LocalRegistrationID: s.localRegistrationID,
|
||||
NeedsRefresh: s.needsRefresh,
|
||||
PendingKeyExchange: pendingKeyExchange,
|
||||
PendingPreKey: s.pendingPreKey.structure(),
|
||||
PreviousCounter: s.previousCounter,
|
||||
ReceiverChains: receiverChains,
|
||||
RemoteIdentityPublic: s.remoteIdentityPublic.Serialize(),
|
||||
RemoteRegistrationID: s.remoteRegistrationID,
|
||||
RootKey: s.rootKey.Bytes(),
|
||||
SenderBaseKey: s.senderBaseKey.Serialize(),
|
||||
SenderChain: s.senderChain.structure(),
|
||||
SessionVersion: s.sessionVersion,
|
||||
}
|
||||
}
|
||||
112
vendor/go.mau.fi/libsignal/state/record/SignedPreKeyRecord.go
vendored
Normal file
112
vendor/go.mau.fi/libsignal/state/record/SignedPreKeyRecord.go
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
package record
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/util/bytehelper"
|
||||
)
|
||||
|
||||
// SignedPreKeySerializer is an interface for serializing and deserializing
|
||||
// SignedPreKey objects into bytes. An implementation of this interface should be
|
||||
// used to encode/decode the object into JSON, Protobuffers, etc.
|
||||
type SignedPreKeySerializer interface {
|
||||
Serialize(signedPreKey *SignedPreKeyStructure) []byte
|
||||
Deserialize(serialized []byte) (*SignedPreKeyStructure, error)
|
||||
}
|
||||
|
||||
// NewSignedPreKeyFromBytes will return a signed prekey record from the given
|
||||
// bytes using the given serializer.
|
||||
func NewSignedPreKeyFromBytes(serialized []byte, serializer SignedPreKeySerializer) (*SignedPreKey, error) {
|
||||
// Use the given serializer to decode the signal message.
|
||||
signedPreKeyStructure, err := serializer.Deserialize(serialized)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return NewSignedPreKeyFromStruct(signedPreKeyStructure, serializer)
|
||||
}
|
||||
|
||||
// NewSignedPreKeyFromStruct returns a SignedPreKey record using the given
|
||||
// serializable structure.
|
||||
func NewSignedPreKeyFromStruct(structure *SignedPreKeyStructure,
|
||||
serializer SignedPreKeySerializer) (*SignedPreKey, error) {
|
||||
|
||||
// Create the signed prekey record from the structure.
|
||||
signedPreKey := &SignedPreKey{
|
||||
structure: *structure,
|
||||
serializer: serializer,
|
||||
signature: bytehelper.SliceToArray64(structure.Signature),
|
||||
}
|
||||
|
||||
// Generate the ECC key from bytes.
|
||||
publicKey := ecc.NewDjbECPublicKey(bytehelper.SliceToArray(structure.PublicKey))
|
||||
privateKey := ecc.NewDjbECPrivateKey(bytehelper.SliceToArray(structure.PrivateKey))
|
||||
keyPair := ecc.NewECKeyPair(publicKey, privateKey)
|
||||
signedPreKey.keyPair = keyPair
|
||||
|
||||
return signedPreKey, nil
|
||||
}
|
||||
|
||||
// NewSignedPreKey record creates a new signed pre key record
|
||||
// with the given properties.
|
||||
func NewSignedPreKey(id uint32, timestamp int64, keyPair *ecc.ECKeyPair,
|
||||
sig [64]byte, serializer SignedPreKeySerializer) *SignedPreKey {
|
||||
|
||||
return &SignedPreKey{
|
||||
structure: SignedPreKeyStructure{
|
||||
ID: id,
|
||||
Timestamp: timestamp,
|
||||
PublicKey: keyPair.PublicKey().Serialize(),
|
||||
PrivateKey: bytehelper.ArrayToSlice(keyPair.PrivateKey().Serialize()),
|
||||
Signature: bytehelper.ArrayToSlice64(sig),
|
||||
},
|
||||
keyPair: keyPair,
|
||||
signature: sig,
|
||||
serializer: serializer,
|
||||
}
|
||||
}
|
||||
|
||||
// SignedPreKeyStructure is a flat structure of a signed pre key, used
|
||||
// for serialization and deserialization.
|
||||
type SignedPreKeyStructure struct {
|
||||
ID uint32
|
||||
PublicKey []byte
|
||||
PrivateKey []byte
|
||||
Signature []byte
|
||||
Timestamp int64
|
||||
}
|
||||
|
||||
// SignedPreKey record is a structure for storing a signed
|
||||
// pre key in a SignedPreKey store.
|
||||
type SignedPreKey struct {
|
||||
structure SignedPreKeyStructure
|
||||
keyPair *ecc.ECKeyPair
|
||||
signature [64]byte
|
||||
serializer SignedPreKeySerializer
|
||||
}
|
||||
|
||||
// ID returns the record's id.
|
||||
func (s *SignedPreKey) ID() uint32 {
|
||||
return s.structure.ID
|
||||
}
|
||||
|
||||
// Timestamp returns the record's timestamp
|
||||
func (s *SignedPreKey) Timestamp() int64 {
|
||||
return s.structure.Timestamp
|
||||
}
|
||||
|
||||
// KeyPair returns the signed pre key record's key pair.
|
||||
func (s *SignedPreKey) KeyPair() *ecc.ECKeyPair {
|
||||
return s.keyPair
|
||||
}
|
||||
|
||||
// Signature returns the record's signed prekey signature.
|
||||
func (s *SignedPreKey) Signature() [64]byte {
|
||||
return s.signature
|
||||
}
|
||||
|
||||
// Serialize uses the SignedPreKey serializer to return the SignedPreKey
|
||||
// as serialized bytes.
|
||||
func (s *SignedPreKey) Serialize() []byte {
|
||||
structure := s.structure
|
||||
return s.serializer.Serialize(&structure)
|
||||
}
|
||||
69
vendor/go.mau.fi/libsignal/state/record/UnacknowledgedPreKey.go
vendored
Normal file
69
vendor/go.mau.fi/libsignal/state/record/UnacknowledgedPreKey.go
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
package record
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/util/optional"
|
||||
)
|
||||
|
||||
// NewUnackPreKeyMessageItems returns message items that are unacknowledged.
|
||||
func NewUnackPreKeyMessageItems(preKeyID *optional.Uint32, signedPreKeyID uint32,
|
||||
baseKey ecc.ECPublicKeyable) *UnackPreKeyMessageItems {
|
||||
|
||||
return &UnackPreKeyMessageItems{
|
||||
preKeyID: preKeyID,
|
||||
signedPreKeyID: signedPreKeyID,
|
||||
baseKey: baseKey,
|
||||
}
|
||||
}
|
||||
|
||||
// NewUnackPreKeyMessageItemsFromStruct will return a new unacknowledged prekey
|
||||
// message items object from the given structure.
|
||||
func NewUnackPreKeyMessageItemsFromStruct(structure *UnackPreKeyMessageItemsStructure) *UnackPreKeyMessageItems {
|
||||
baseKey, _ := ecc.DecodePoint(structure.BaseKey, 0)
|
||||
return NewUnackPreKeyMessageItems(
|
||||
structure.PreKeyID,
|
||||
structure.SignedPreKeyID,
|
||||
baseKey,
|
||||
)
|
||||
}
|
||||
|
||||
// UnackPreKeyMessageItemsStructure is a serializable structure for unackowledged
|
||||
// prekey message items.
|
||||
type UnackPreKeyMessageItemsStructure struct {
|
||||
PreKeyID *optional.Uint32
|
||||
SignedPreKeyID uint32
|
||||
BaseKey []byte
|
||||
}
|
||||
|
||||
// UnackPreKeyMessageItems is a structure for messages that have not been
|
||||
// acknowledged.
|
||||
type UnackPreKeyMessageItems struct {
|
||||
preKeyID *optional.Uint32
|
||||
signedPreKeyID uint32
|
||||
baseKey ecc.ECPublicKeyable
|
||||
}
|
||||
|
||||
// PreKeyID returns the prekey id of the unacknowledged message.
|
||||
func (u *UnackPreKeyMessageItems) PreKeyID() *optional.Uint32 {
|
||||
return u.preKeyID
|
||||
}
|
||||
|
||||
// SignedPreKeyID returns the signed prekey id of the unacknowledged message.
|
||||
func (u *UnackPreKeyMessageItems) SignedPreKeyID() uint32 {
|
||||
return u.signedPreKeyID
|
||||
}
|
||||
|
||||
// BaseKey returns the ECC public key of the unacknowledged message.
|
||||
func (u *UnackPreKeyMessageItems) BaseKey() ecc.ECPublicKeyable {
|
||||
return u.baseKey
|
||||
}
|
||||
|
||||
// structure will return a serializable base structure
|
||||
// for unacknowledged prekey message items.
|
||||
func (u *UnackPreKeyMessageItems) structure() *UnackPreKeyMessageItemsStructure {
|
||||
return &UnackPreKeyMessageItemsStructure{
|
||||
PreKeyID: u.preKeyID,
|
||||
SignedPreKeyID: u.signedPreKeyID,
|
||||
BaseKey: u.baseKey.Serialize(),
|
||||
}
|
||||
}
|
||||
3
vendor/go.mau.fi/libsignal/state/store/Doc.go
vendored
Normal file
3
vendor/go.mau.fi/libsignal/state/store/Doc.go
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
// Package store provides the storage interfaces for storing the state of
|
||||
// ongoing double ratchet sessions and keys.
|
||||
package store
|
||||
29
vendor/go.mau.fi/libsignal/state/store/IdentityKeyStore.go
vendored
Normal file
29
vendor/go.mau.fi/libsignal/state/store/IdentityKeyStore.go
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/keys/identity"
|
||||
"go.mau.fi/libsignal/protocol"
|
||||
)
|
||||
|
||||
// IdentityKey provides an interface to identity information.
|
||||
type IdentityKey interface {
|
||||
// Get the local client's identity key pair.
|
||||
GetIdentityKeyPair() *identity.KeyPair
|
||||
|
||||
// Return the local client's registration ID.
|
||||
//
|
||||
// Clients should maintain a registration ID, a random number between 1 and 16380
|
||||
// that's generated once at install time.
|
||||
GetLocalRegistrationId() uint32
|
||||
|
||||
// Save a remote client's identity key in our identity store.
|
||||
SaveIdentity(address *protocol.SignalAddress, identityKey *identity.Key)
|
||||
|
||||
// Verify a remote client's identity key.
|
||||
//
|
||||
// Determine whether a remote client's identity is trusted. Trust is based on
|
||||
// 'trust on first use'. This means that an identity key is considered 'trusted'
|
||||
// if there is no entry for the recipient in the local store, or if it matches the
|
||||
// saved key for a recipient in the local store.
|
||||
IsTrustedIdentity(address *protocol.SignalAddress, identityKey *identity.Key) bool
|
||||
}
|
||||
21
vendor/go.mau.fi/libsignal/state/store/MessageKeyStore.go
vendored
Normal file
21
vendor/go.mau.fi/libsignal/state/store/MessageKeyStore.go
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/keys/message"
|
||||
)
|
||||
|
||||
// MessageKey store is an interface describing the optional local storage
|
||||
// of message keys.
|
||||
type MessageKey interface {
|
||||
// Load a local message key by id
|
||||
LoadMessageKey(keyID uint32) *message.Keys
|
||||
|
||||
// Store a local message key
|
||||
StoreMessageKey(keyID uint32, key *message.Keys)
|
||||
|
||||
// Check to see if the store contains a message key with id.
|
||||
ContainsMessageKey(keyID uint32) bool
|
||||
|
||||
// Delete a message key from local storage.
|
||||
RemoveMessageKey(keyID uint32)
|
||||
}
|
||||
21
vendor/go.mau.fi/libsignal/state/store/PreKeyStore.go
vendored
Normal file
21
vendor/go.mau.fi/libsignal/state/store/PreKeyStore.go
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/state/record"
|
||||
)
|
||||
|
||||
// PreKey store is an interface describing the local storage
|
||||
// of PreKeyRecords
|
||||
type PreKey interface {
|
||||
// Load a local PreKeyRecord
|
||||
LoadPreKey(preKeyID uint32) *record.PreKey
|
||||
|
||||
// Store a local PreKeyRecord
|
||||
StorePreKey(preKeyID uint32, preKeyRecord *record.PreKey)
|
||||
|
||||
// Check to see if the store contains a PreKeyRecord
|
||||
ContainsPreKey(preKeyID uint32) bool
|
||||
|
||||
// Delete a PreKeyRecord from local storage.
|
||||
RemovePreKey(preKeyID uint32)
|
||||
}
|
||||
17
vendor/go.mau.fi/libsignal/state/store/SessionStore.go
vendored
Normal file
17
vendor/go.mau.fi/libsignal/state/store/SessionStore.go
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/protocol"
|
||||
"go.mau.fi/libsignal/state/record"
|
||||
)
|
||||
|
||||
// Session store is an interface for the persistent storage of session
|
||||
// state information for remote clients.
|
||||
type Session interface {
|
||||
LoadSession(address *protocol.SignalAddress) *record.Session
|
||||
GetSubDeviceSessions(name string) []uint32
|
||||
StoreSession(remoteAddress *protocol.SignalAddress, record *record.Session)
|
||||
ContainsSession(remoteAddress *protocol.SignalAddress) bool
|
||||
DeleteSession(remoteAddress *protocol.SignalAddress)
|
||||
DeleteAllSessions()
|
||||
}
|
||||
15
vendor/go.mau.fi/libsignal/state/store/SignalProtocolStore.go
vendored
Normal file
15
vendor/go.mau.fi/libsignal/state/store/SignalProtocolStore.go
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/groups/state/store"
|
||||
)
|
||||
|
||||
// SignalProtocol store is an interface that implements the
|
||||
// methods for all stores needed in the Signal Protocol.
|
||||
type SignalProtocol interface {
|
||||
IdentityKey
|
||||
PreKey
|
||||
Session
|
||||
SignedPreKey
|
||||
store.SenderKey
|
||||
}
|
||||
24
vendor/go.mau.fi/libsignal/state/store/SignedPreKeyStore.go
vendored
Normal file
24
vendor/go.mau.fi/libsignal/state/store/SignedPreKeyStore.go
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"go.mau.fi/libsignal/state/record"
|
||||
)
|
||||
|
||||
// SignedPreKey store is an interface that describes how to persistently
|
||||
// store signed PreKeys.
|
||||
type SignedPreKey interface {
|
||||
// LoadSignedPreKey loads a local SignedPreKeyRecord
|
||||
LoadSignedPreKey(signedPreKeyID uint32) *record.SignedPreKey
|
||||
|
||||
// LoadSignedPreKeys loads all local SignedPreKeyRecords
|
||||
LoadSignedPreKeys() []*record.SignedPreKey
|
||||
|
||||
// Store a local SignedPreKeyRecord
|
||||
StoreSignedPreKey(signedPreKeyID uint32, record *record.SignedPreKey)
|
||||
|
||||
// Check to see if store contains the given record
|
||||
ContainsSignedPreKey(signedPreKeyID uint32) bool
|
||||
|
||||
// Delete a SignedPreKeyRecord from local storage
|
||||
RemoveSignedPreKey(signedPreKeyID uint32)
|
||||
}
|
||||
97
vendor/go.mau.fi/libsignal/util/bytehelper/ByteHelper.go
vendored
Normal file
97
vendor/go.mau.fi/libsignal/util/bytehelper/ByteHelper.go
vendored
Normal file
@@ -0,0 +1,97 @@
|
||||
package bytehelper
|
||||
|
||||
import (
|
||||
"errors"
|
||||
)
|
||||
|
||||
// SliceToArray will convert byte slice to a 32 byte array
|
||||
func SliceToArray(bytes []byte) [32]byte {
|
||||
var byteArray [32]byte
|
||||
copy(byteArray[:], bytes)
|
||||
return byteArray
|
||||
}
|
||||
|
||||
// SliceToArray64 will convert byte slice to a 64 byte array
|
||||
func SliceToArray64(bytes []byte) [64]byte {
|
||||
var byteArray [64]byte
|
||||
copy(byteArray[:], bytes)
|
||||
return byteArray
|
||||
}
|
||||
|
||||
// ArrayToSlice will convert a 32 byte array to byte slice
|
||||
func ArrayToSlice(bytes [32]byte) []byte {
|
||||
return bytes[:]
|
||||
}
|
||||
|
||||
// ArrayToSlice64 will convert a 64 byte array to byte slice
|
||||
func ArrayToSlice64(bytes [64]byte) []byte {
|
||||
return bytes[:]
|
||||
}
|
||||
|
||||
// Split will take the given byte array and split it into half,
|
||||
// with the first half being "firstLength" in size and the second
|
||||
// half "secondLength" in size.
|
||||
func Split(input []byte, firstLength, secondLength int) [][]byte {
|
||||
parts := make([][]byte, 2)
|
||||
|
||||
parts[0] = make([]byte, firstLength)
|
||||
copy(parts[0], input[:firstLength])
|
||||
|
||||
parts[1] = make([]byte, secondLength)
|
||||
copy(parts[1], input[firstLength:])
|
||||
|
||||
return parts
|
||||
}
|
||||
|
||||
// SplitThree will take the given byte array and split it into thirds,
|
||||
// with the first third being "firstLength" in size, the second third
|
||||
// being "secondLength" in size, and the last third being "thirdLength"
|
||||
// in size.
|
||||
func SplitThree(input []byte, firstLength, secondLength, thirdLength int) ([][]byte, error) {
|
||||
if input == nil || firstLength < 0 || secondLength < 0 || thirdLength < 0 ||
|
||||
len(input) < firstLength+secondLength+thirdLength {
|
||||
|
||||
return nil, errors.New("Input too small: " + string(input))
|
||||
}
|
||||
|
||||
parts := make([][]byte, 3)
|
||||
|
||||
parts[0] = make([]byte, firstLength)
|
||||
copy(parts[0], input[:firstLength])
|
||||
|
||||
parts[1] = make([]byte, secondLength)
|
||||
copy(parts[1], input[firstLength:][:secondLength])
|
||||
|
||||
parts[2] = make([]byte, thirdLength)
|
||||
copy(parts[2], input[firstLength+secondLength:])
|
||||
|
||||
return parts, nil
|
||||
}
|
||||
|
||||
// Trim will trim the given byte array to the given length.
|
||||
func Trim(input []byte, length int) []byte {
|
||||
result := make([]byte, length)
|
||||
copy(result, input[:length])
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Bytes5ToInt64 will convert the given byte array and offset to an int64.
|
||||
func Bytes5ToInt64(bytes []byte, offset int) int64 {
|
||||
|
||||
value := (int64(bytes[offset]&0xff) << 32) |
|
||||
(int64(bytes[offset+1]&0xff) << 24) |
|
||||
(int64(bytes[offset+2]&0xff) << 16) |
|
||||
(int64(bytes[offset+3]&0xff) << 8) |
|
||||
int64(bytes[offset+4]&0xff)
|
||||
|
||||
return value
|
||||
}
|
||||
|
||||
// CopySlice returns a copy of the given bytes.
|
||||
func CopySlice(bytes []byte) []byte {
|
||||
cp := make([]byte, len(bytes))
|
||||
copy(cp, bytes)
|
||||
|
||||
return cp
|
||||
}
|
||||
40
vendor/go.mau.fi/libsignal/util/errorhelper/ErrorHelper.go
vendored
Normal file
40
vendor/go.mau.fi/libsignal/util/errorhelper/ErrorHelper.go
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
package errorhelper
|
||||
|
||||
// NewMultiError returns a new MultiError object.
|
||||
func NewMultiError() *MultiError {
|
||||
return &MultiError{
|
||||
errors: []error{},
|
||||
}
|
||||
}
|
||||
|
||||
// MultiError is a structure for holding multiple errors so they
|
||||
// can be checked at a later point.
|
||||
type MultiError struct {
|
||||
errors []error
|
||||
}
|
||||
|
||||
// Add will add the given error if it is not nil.
|
||||
func (m *MultiError) Add(err error) {
|
||||
if err != nil {
|
||||
m.errors = append(m.errors, err)
|
||||
}
|
||||
}
|
||||
|
||||
// HasErrors will return true if any non-nil errors have been
|
||||
// added.
|
||||
func (m *MultiError) HasErrors() bool {
|
||||
if len(m.errors) > 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// Error will print the first error is encountered.
|
||||
func (m *MultiError) Error() string {
|
||||
if !m.HasErrors() {
|
||||
return ""
|
||||
}
|
||||
|
||||
return m.errors[0].Error()
|
||||
}
|
||||
95
vendor/go.mau.fi/libsignal/util/keyhelper/KeyHelper.go
vendored
Normal file
95
vendor/go.mau.fi/libsignal/util/keyhelper/KeyHelper.go
vendored
Normal file
@@ -0,0 +1,95 @@
|
||||
// Package keyhelper is based on: https://github.com/WhisperSystems/libsignal-protocol-java/blob/master/java/src/main/java/org/whispersystems/libsignal/util/KeyHelper.java
|
||||
package keyhelper
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/binary"
|
||||
"time"
|
||||
|
||||
"go.mau.fi/libsignal/ecc"
|
||||
"go.mau.fi/libsignal/keys/identity"
|
||||
"go.mau.fi/libsignal/state/record"
|
||||
)
|
||||
|
||||
// GenerateIdentityKeyPair generates an identity keypair used for
|
||||
// signing. Clients should only do this once at install time.
|
||||
func GenerateIdentityKeyPair() (*identity.KeyPair, error) {
|
||||
keyPair, err := ecc.GenerateKeyPair()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
publicKey := identity.NewKey(keyPair.PublicKey())
|
||||
return identity.NewKeyPair(publicKey, keyPair.PrivateKey()), nil
|
||||
}
|
||||
|
||||
// GeneratePreKeys generates a list of PreKeys. Client shsould do this at
|
||||
// install time, and subsequently any time the list of PreKeys stored on
|
||||
// the server runs low.
|
||||
//
|
||||
// PreKeys IDs are shorts, so they will eventually be repeated. Clients
|
||||
// should store PreKeys in a circular buffer, so that they are repeated
|
||||
// as infrequently as possible.
|
||||
func GeneratePreKeys(start int, count int, serializer record.PreKeySerializer) ([]*record.PreKey, error) {
|
||||
var preKeys []*record.PreKey
|
||||
|
||||
for i := start; i <= count; i++ {
|
||||
key, err := ecc.GenerateKeyPair()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
preKeys = append(preKeys, record.NewPreKey(uint32(i), key, serializer))
|
||||
}
|
||||
|
||||
return preKeys, nil
|
||||
}
|
||||
|
||||
// GenerateLastResortKey will generate the last resort PreKey. Clients should
|
||||
// do this only once, at install time, and durably store it for the length
|
||||
// of the install.
|
||||
func GenerateLastResortKey(serializer record.PreKeySerializer) (*record.PreKey, error) {
|
||||
keyPair, err := ecc.GenerateKeyPair()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return record.NewPreKey(0, keyPair, serializer), nil
|
||||
}
|
||||
|
||||
// GenerateSignedPreKey generates a signed PreKey.
|
||||
func GenerateSignedPreKey(identityKeyPair *identity.KeyPair, signedPreKeyID uint32, serializer record.SignedPreKeySerializer) (*record.SignedPreKey, error) {
|
||||
keyPair, err := ecc.GenerateKeyPair()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
signature := ecc.CalculateSignature(identityKeyPair.PrivateKey(), keyPair.PublicKey().Serialize())
|
||||
timestamp := time.Now().Unix()
|
||||
|
||||
return record.NewSignedPreKey(signedPreKeyID, timestamp, keyPair, signature, serializer), nil
|
||||
}
|
||||
|
||||
// GenerateRegistrationID generates a registration ID. Clients should only do
|
||||
// this once, at install time.
|
||||
func GenerateRegistrationID() uint32 {
|
||||
var n uint32
|
||||
binary.Read(rand.Reader, binary.LittleEndian, &n)
|
||||
|
||||
return n
|
||||
}
|
||||
|
||||
//---------- Group Stuff ----------------
|
||||
|
||||
func GenerateSenderSigningKey() (*ecc.ECKeyPair, error) {
|
||||
return ecc.GenerateKeyPair()
|
||||
}
|
||||
|
||||
func GenerateSenderKey() []byte {
|
||||
randBytes := make([]byte, 32)
|
||||
rand.Read(randBytes)
|
||||
return randBytes
|
||||
}
|
||||
|
||||
func GenerateSenderKeyID() uint32 {
|
||||
return GenerateRegistrationID()
|
||||
}
|
||||
|
||||
//---------- End Group Stuff --------------
|
||||
4
vendor/go.mau.fi/libsignal/util/medium/Medium.go
vendored
Normal file
4
vendor/go.mau.fi/libsignal/util/medium/Medium.go
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
package medium
|
||||
|
||||
// MaxValue is the maximum possible integer value.
|
||||
const MaxValue uint32 = 0xFFFFFF
|
||||
17
vendor/go.mau.fi/libsignal/util/optional/Integer.go
vendored
Normal file
17
vendor/go.mau.fi/libsignal/util/optional/Integer.go
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
package optional
|
||||
|
||||
// NewOptionalUint32 returns an optional Uint32 structure.
|
||||
func NewOptionalUint32(value uint32) *Uint32 {
|
||||
return &Uint32{Value: value, IsEmpty: false}
|
||||
}
|
||||
|
||||
func NewEmptyUint32() *Uint32 {
|
||||
return &Uint32{IsEmpty: true}
|
||||
}
|
||||
|
||||
// Uint32 is a simple structure for Uint32 values that can
|
||||
// optionally be nil.
|
||||
type Uint32 struct {
|
||||
Value uint32
|
||||
IsEmpty bool
|
||||
}
|
||||
374
vendor/go.mau.fi/whatsmeow/LICENSE
vendored
Normal file
374
vendor/go.mau.fi/whatsmeow/LICENSE
vendored
Normal file
@@ -0,0 +1,374 @@
|
||||
Mozilla Public License Version 2.0
|
||||
==================================
|
||||
|
||||
1. Definitions
|
||||
--------------
|
||||
|
||||
1.1. "Contributor"
|
||||
means each individual or legal entity that creates, contributes to
|
||||
the creation of, or owns Covered Software.
|
||||
|
||||
1.2. "Contributor Version"
|
||||
means the combination of the Contributions of others (if any) used
|
||||
by a Contributor and that particular Contributor's Contribution.
|
||||
|
||||
1.3. "Contribution"
|
||||
means Covered Software of a particular Contributor.
|
||||
|
||||
1.4. "Covered Software"
|
||||
means Source Code Form to which the initial Contributor has attached
|
||||
the notice in Exhibit A, the Executable Form of such Source Code
|
||||
Form, and Modifications of such Source Code Form, in each case
|
||||
including portions thereof.
|
||||
|
||||
1.5. "Incompatible With Secondary Licenses"
|
||||
means
|
||||
|
||||
(a) that the initial Contributor has attached the notice described
|
||||
in Exhibit B to the Covered Software; or
|
||||
|
||||
(b) that the Covered Software was made available under the terms of
|
||||
version 1.1 or earlier of the License, but not also under the
|
||||
terms of a Secondary License.
|
||||
|
||||
1.6. "Executable Form"
|
||||
means any form of the work other than Source Code Form.
|
||||
|
||||
1.7. "Larger Work"
|
||||
means a work that combines Covered Software with other material, in
|
||||
a separate file or files, that is not Covered Software.
|
||||
|
||||
1.8. "License"
|
||||
means this document.
|
||||
|
||||
1.9. "Licensable"
|
||||
means having the right to grant, to the maximum extent possible,
|
||||
whether at the time of the initial grant or subsequently, any and
|
||||
all of the rights conveyed by this License.
|
||||
|
||||
1.10. "Modifications"
|
||||
means any of the following:
|
||||
|
||||
(a) any file in Source Code Form that results from an addition to,
|
||||
deletion from, or modification of the contents of Covered
|
||||
Software; or
|
||||
|
||||
(b) any new file in Source Code Form that contains any Covered
|
||||
Software.
|
||||
|
||||
1.11. "Patent Claims" of a Contributor
|
||||
means any patent claim(s), including without limitation, method,
|
||||
process, and apparatus claims, in any patent Licensable by such
|
||||
Contributor that would be infringed, but for the grant of the
|
||||
License, by the making, using, selling, offering for sale, having
|
||||
made, import, or transfer of either its Contributions or its
|
||||
Contributor Version.
|
||||
|
||||
1.12. "Secondary License"
|
||||
means either the GNU General Public License, Version 2.0, the GNU
|
||||
Lesser General Public License, Version 2.1, the GNU Affero General
|
||||
Public License, Version 3.0, or any later versions of those
|
||||
licenses.
|
||||
|
||||
1.13. "Source Code Form"
|
||||
means the form of the work preferred for making modifications.
|
||||
|
||||
1.14. "You" (or "Your")
|
||||
means an individual or a legal entity exercising rights under this
|
||||
License. For legal entities, "You" includes any entity that
|
||||
controls, is controlled by, or is under common control with You. For
|
||||
purposes of this definition, "control" means (a) the power, direct
|
||||
or indirect, to cause the direction or management of such entity,
|
||||
whether by contract or otherwise, or (b) ownership of more than
|
||||
fifty percent (50%) of the outstanding shares or beneficial
|
||||
ownership of such entity.
|
||||
|
||||
2. License Grants and Conditions
|
||||
--------------------------------
|
||||
|
||||
2.1. Grants
|
||||
|
||||
Each Contributor hereby grants You a world-wide, royalty-free,
|
||||
non-exclusive license:
|
||||
|
||||
(a) under intellectual property rights (other than patent or trademark)
|
||||
Licensable by such Contributor to use, reproduce, make available,
|
||||
modify, display, perform, distribute, and otherwise exploit its
|
||||
Contributions, either on an unmodified basis, with Modifications, or
|
||||
as part of a Larger Work; and
|
||||
|
||||
(b) under Patent Claims of such Contributor to make, use, sell, offer
|
||||
for sale, have made, import, and otherwise transfer either its
|
||||
Contributions or its Contributor Version.
|
||||
|
||||
2.2. Effective Date
|
||||
|
||||
The licenses granted in Section 2.1 with respect to any Contribution
|
||||
become effective for each Contribution on the date the Contributor first
|
||||
distributes such Contribution.
|
||||
|
||||
2.3. Limitations on Grant Scope
|
||||
|
||||
The licenses granted in this Section 2 are the only rights granted under
|
||||
this License. No additional rights or licenses will be implied from the
|
||||
distribution or licensing of Covered Software under this License.
|
||||
Notwithstanding Section 2.1(b) above, no patent license is granted by a
|
||||
Contributor:
|
||||
|
||||
(a) for any code that a Contributor has removed from Covered Software;
|
||||
or
|
||||
|
||||
(b) for infringements caused by: (i) Your and any other third party's
|
||||
modifications of Covered Software, or (ii) the combination of its
|
||||
Contributions with other software (except as part of its Contributor
|
||||
Version); or
|
||||
|
||||
(c) under Patent Claims infringed by Covered Software in the absence of
|
||||
its Contributions.
|
||||
|
||||
This License does not grant any rights in the trademarks, service marks,
|
||||
or logos of any Contributor (except as may be necessary to comply with
|
||||
the notice requirements in Section 3.4).
|
||||
|
||||
2.4. Subsequent Licenses
|
||||
|
||||
No Contributor makes additional grants as a result of Your choice to
|
||||
distribute the Covered Software under a subsequent version of this
|
||||
License (see Section 10.2) or under the terms of a Secondary License (if
|
||||
permitted under the terms of Section 3.3).
|
||||
|
||||
2.5. Representation
|
||||
|
||||
Each Contributor represents that the Contributor believes its
|
||||
Contributions are its original creation(s) or it has sufficient rights
|
||||
to grant the rights to its Contributions conveyed by this License.
|
||||
|
||||
2.6. Fair Use
|
||||
|
||||
This License is not intended to limit any rights You have under
|
||||
applicable copyright doctrines of fair use, fair dealing, or other
|
||||
equivalents.
|
||||
|
||||
2.7. Conditions
|
||||
|
||||
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
|
||||
in Section 2.1.
|
||||
|
||||
3. Responsibilities
|
||||
-------------------
|
||||
|
||||
3.1. Distribution of Source Form
|
||||
|
||||
All distribution of Covered Software in Source Code Form, including any
|
||||
Modifications that You create or to which You contribute, must be under
|
||||
the terms of this License. You must inform recipients that the Source
|
||||
Code Form of the Covered Software is governed by the terms of this
|
||||
License, and how they can obtain a copy of this License. You may not
|
||||
attempt to alter or restrict the recipients' rights in the Source Code
|
||||
Form.
|
||||
|
||||
3.2. Distribution of Executable Form
|
||||
|
||||
If You distribute Covered Software in Executable Form then:
|
||||
|
||||
(a) such Covered Software must also be made available in Source Code
|
||||
Form, as described in Section 3.1, and You must inform recipients of
|
||||
the Executable Form how they can obtain a copy of such Source Code
|
||||
Form by reasonable means in a timely manner, at a charge no more
|
||||
than the cost of distribution to the recipient; and
|
||||
|
||||
(b) You may distribute such Executable Form under the terms of this
|
||||
License, or sublicense it under different terms, provided that the
|
||||
license for the Executable Form does not attempt to limit or alter
|
||||
the recipients' rights in the Source Code Form under this License.
|
||||
|
||||
3.3. Distribution of a Larger Work
|
||||
|
||||
You may create and distribute a Larger Work under terms of Your choice,
|
||||
provided that You also comply with the requirements of this License for
|
||||
the Covered Software. If the Larger Work is a combination of Covered
|
||||
Software with a work governed by one or more Secondary Licenses, and the
|
||||
Covered Software is not Incompatible With Secondary Licenses, this
|
||||
License permits You to additionally distribute such Covered Software
|
||||
under the terms of such Secondary License(s), so that the recipient of
|
||||
the Larger Work may, at their option, further distribute the Covered
|
||||
Software under the terms of either this License or such Secondary
|
||||
License(s).
|
||||
|
||||
3.4. Notices
|
||||
|
||||
You may not remove or alter the substance of any license notices
|
||||
(including copyright notices, patent notices, disclaimers of warranty,
|
||||
or limitations of liability) contained within the Source Code Form of
|
||||
the Covered Software, except that You may alter any license notices to
|
||||
the extent required to remedy known factual inaccuracies.
|
||||
|
||||
3.5. Application of Additional Terms
|
||||
|
||||
You may choose to offer, and to charge a fee for, warranty, support,
|
||||
indemnity or liability obligations to one or more recipients of Covered
|
||||
Software. However, You may do so only on Your own behalf, and not on
|
||||
behalf of any Contributor. You must make it absolutely clear that any
|
||||
such warranty, support, indemnity, or liability obligation is offered by
|
||||
You alone, and You hereby agree to indemnify every Contributor for any
|
||||
liability incurred by such Contributor as a result of warranty, support,
|
||||
indemnity or liability terms You offer. You may include additional
|
||||
disclaimers of warranty and limitations of liability specific to any
|
||||
jurisdiction.
|
||||
|
||||
4. Inability to Comply Due to Statute or Regulation
|
||||
---------------------------------------------------
|
||||
|
||||
If it is impossible for You to comply with any of the terms of this
|
||||
License with respect to some or all of the Covered Software due to
|
||||
statute, judicial order, or regulation then You must: (a) comply with
|
||||
the terms of this License to the maximum extent possible; and (b)
|
||||
describe the limitations and the code they affect. Such description must
|
||||
be placed in a text file included with all distributions of the Covered
|
||||
Software under this License. Except to the extent prohibited by statute
|
||||
or regulation, such description must be sufficiently detailed for a
|
||||
recipient of ordinary skill to be able to understand it.
|
||||
|
||||
5. Termination
|
||||
--------------
|
||||
|
||||
5.1. The rights granted under this License will terminate automatically
|
||||
if You fail to comply with any of its terms. However, if You become
|
||||
compliant, then the rights granted under this License from a particular
|
||||
Contributor are reinstated (a) provisionally, unless and until such
|
||||
Contributor explicitly and finally terminates Your grants, and (b) on an
|
||||
ongoing basis, if such Contributor fails to notify You of the
|
||||
non-compliance by some reasonable means prior to 60 days after You have
|
||||
come back into compliance. Moreover, Your grants from a particular
|
||||
Contributor are reinstated on an ongoing basis if such Contributor
|
||||
notifies You of the non-compliance by some reasonable means, this is the
|
||||
first time You have received notice of non-compliance with this License
|
||||
from such Contributor, and You become compliant prior to 30 days after
|
||||
Your receipt of the notice.
|
||||
|
||||
5.2. If You initiate litigation against any entity by asserting a patent
|
||||
infringement claim (excluding declaratory judgment actions,
|
||||
counter-claims, and cross-claims) alleging that a Contributor Version
|
||||
directly or indirectly infringes any patent, then the rights granted to
|
||||
You by any and all Contributors for the Covered Software under Section
|
||||
2.1 of this License shall terminate.
|
||||
|
||||
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
|
||||
end user license agreements (excluding distributors and resellers) which
|
||||
have been validly granted by You or Your distributors under this License
|
||||
prior to termination shall survive termination.
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 6. Disclaimer of Warranty *
|
||||
* ------------------------- *
|
||||
* *
|
||||
* Covered Software is provided under this License on an "as is" *
|
||||
* basis, without warranty of any kind, either expressed, implied, or *
|
||||
* statutory, including, without limitation, warranties that the *
|
||||
* Covered Software is free of defects, merchantable, fit for a *
|
||||
* particular purpose or non-infringing. The entire risk as to the *
|
||||
* quality and performance of the Covered Software is with You. *
|
||||
* Should any Covered Software prove defective in any respect, You *
|
||||
* (not any Contributor) assume the cost of any necessary servicing, *
|
||||
* repair, or correction. This disclaimer of warranty constitutes an *
|
||||
* essential part of this License. No use of any Covered Software is *
|
||||
* authorized under this License except under this disclaimer. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
************************************************************************
|
||||
* *
|
||||
* 7. Limitation of Liability *
|
||||
* -------------------------- *
|
||||
* *
|
||||
* Under no circumstances and under no legal theory, whether tort *
|
||||
* (including negligence), contract, or otherwise, shall any *
|
||||
* Contributor, or anyone who distributes Covered Software as *
|
||||
* permitted above, be liable to You for any direct, indirect, *
|
||||
* special, incidental, or consequential damages of any character *
|
||||
* including, without limitation, damages for lost profits, loss of *
|
||||
* goodwill, work stoppage, computer failure or malfunction, or any *
|
||||
* and all other commercial damages or losses, even if such party *
|
||||
* shall have been informed of the possibility of such damages. This *
|
||||
* limitation of liability shall not apply to liability for death or *
|
||||
* personal injury resulting from such party's negligence to the *
|
||||
* extent applicable law prohibits such limitation. Some *
|
||||
* jurisdictions do not allow the exclusion or limitation of *
|
||||
* incidental or consequential damages, so this exclusion and *
|
||||
* limitation may not apply to You. *
|
||||
* *
|
||||
************************************************************************
|
||||
|
||||
8. Litigation
|
||||
-------------
|
||||
|
||||
Any litigation relating to this License may be brought only in the
|
||||
courts of a jurisdiction where the defendant maintains its principal
|
||||
place of business and such litigation shall be governed by laws of that
|
||||
jurisdiction, without reference to its conflict-of-law provisions.
|
||||
Nothing in this Section shall prevent a party's ability to bring
|
||||
cross-claims or counter-claims.
|
||||
|
||||
9. Miscellaneous
|
||||
----------------
|
||||
|
||||
This License represents the complete agreement concerning the subject
|
||||
matter hereof. If any provision of this License is held to be
|
||||
unenforceable, such provision shall be reformed only to the extent
|
||||
necessary to make it enforceable. Any law or regulation which provides
|
||||
that the language of a contract shall be construed against the drafter
|
||||
shall not be used to construe this License against a Contributor.
|
||||
|
||||
10. Versions of the License
|
||||
---------------------------
|
||||
|
||||
10.1. New Versions
|
||||
|
||||
Mozilla Foundation is the license steward. Except as provided in Section
|
||||
10.3, no one other than the license steward has the right to modify or
|
||||
publish new versions of this License. Each version will be given a
|
||||
distinguishing version number.
|
||||
|
||||
10.2. Effect of New Versions
|
||||
|
||||
You may distribute the Covered Software under the terms of the version
|
||||
of the License under which You originally received the Covered Software,
|
||||
or under the terms of any subsequent version published by the license
|
||||
steward.
|
||||
|
||||
10.3. Modified Versions
|
||||
|
||||
If you create software not governed by this License, and you want to
|
||||
create a new license for such software, you may create and use a
|
||||
modified version of this License if you rename the license and remove
|
||||
any references to the name of the license steward (except to note that
|
||||
such modified license differs from this License).
|
||||
|
||||
10.4. Distributing Source Code Form that is Incompatible With Secondary
|
||||
Licenses
|
||||
|
||||
If You choose to distribute Source Code Form that is Incompatible With
|
||||
Secondary Licenses under the terms of this version of the License, the
|
||||
notice described in Exhibit B of this License must be attached.
|
||||
|
||||
Exhibit A - Source Code Form License Notice
|
||||
-------------------------------------------
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public
|
||||
License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
If it is not possible or desirable to put the notice in a particular
|
||||
file, then You may include the notice in a location (such as a LICENSE
|
||||
file in a relevant directory) where a recipient would be likely to look
|
||||
for such a notice.
|
||||
|
||||
You may add additional accurate notices of copyright ownership.
|
||||
|
||||
Exhibit B - "Incompatible With Secondary Licenses" Notice
|
||||
---------------------------------------------------------
|
||||
|
||||
This Source Code Form is "Incompatible With Secondary Licenses", as
|
||||
defined by the Mozilla Public License, v. 2.0.
|
||||
|
||||
39
vendor/go.mau.fi/whatsmeow/README.md
vendored
Normal file
39
vendor/go.mau.fi/whatsmeow/README.md
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# whatsmeow
|
||||
[](https://godocs.io/go.mau.fi/whatsmeow)
|
||||
|
||||
whatsmeow is a Go library for the WhatsApp web multidevice API.
|
||||
|
||||
This was initially forked from [go-whatsapp] (MIT license), but large parts of
|
||||
the code have been rewritten for multidevice support. Parts of the code are
|
||||
ported from [WhatsappWeb4j] and [Baileys] (also MIT license).
|
||||
|
||||
[go-whatsapp]: https://github.com/Rhymen/go-whatsapp
|
||||
[WhatsappWeb4j]: https://github.com/Auties00/WhatsappWeb4j
|
||||
[Baileys]: https://github.com/adiwajshing/Baileys
|
||||
|
||||
## Discussion
|
||||
Matrix room: [#whatsmeow:maunium.net](https://matrix.to/#/#whatsmeow:maunium.net)
|
||||
|
||||
## Usage
|
||||
The [godoc](https://godocs.io/go.mau.fi/whatsmeow) includes docs for all methods and event types.
|
||||
There's also a [simple example](https://godocs.io/go.mau.fi/whatsmeow#example-package) at the top.
|
||||
|
||||
Also see [mdtest](./mdtest) for a CLI tool you can easily try out whatsmeow with.
|
||||
|
||||
## Features
|
||||
Most core features are already present:
|
||||
|
||||
* Sending messages to private chats and groups (both text and media)
|
||||
* Receiving all messages
|
||||
* Managing groups and receiving group change events
|
||||
* Joining via invite messages, using and creating invite links
|
||||
* Sending and receiving typing notifications
|
||||
* Sending and receiving delivery and read receipts
|
||||
* Reading app state (contact list, chat pin/mute status, etc)
|
||||
* Sending and handling retry receipts if message decryption fails
|
||||
|
||||
Things that are not yet implemented:
|
||||
|
||||
* Writing app state (contact list, chat pin/mute status, etc)
|
||||
* Sending status messages or broadcast list messages (this is not supported on WhatsApp web either)
|
||||
* Calls
|
||||
193
vendor/go.mau.fi/whatsmeow/appstate.go
vendored
Normal file
193
vendor/go.mau.fi/whatsmeow/appstate.go
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package whatsmeow
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"go.mau.fi/whatsmeow/appstate"
|
||||
waBinary "go.mau.fi/whatsmeow/binary"
|
||||
waProto "go.mau.fi/whatsmeow/binary/proto"
|
||||
"go.mau.fi/whatsmeow/types"
|
||||
"go.mau.fi/whatsmeow/types/events"
|
||||
)
|
||||
|
||||
// FetchAppState fetches updates to the given type of app state. If fullSync is true, the current
|
||||
// cached state will be removed and all app state patches will be re-fetched from the server.
|
||||
func (cli *Client) FetchAppState(name appstate.WAPatchName, fullSync, onlyIfNotSynced bool) error {
|
||||
cli.appStateSyncLock.Lock()
|
||||
defer cli.appStateSyncLock.Unlock()
|
||||
if fullSync {
|
||||
err := cli.Store.AppState.DeleteAppStateVersion(string(name))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to reset app state %s version: %w", name, err)
|
||||
}
|
||||
}
|
||||
version, hash, err := cli.Store.AppState.GetAppStateVersion(string(name))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get app state %s version: %w", name, err)
|
||||
}
|
||||
if version == 0 {
|
||||
fullSync = true
|
||||
} else if onlyIfNotSynced {
|
||||
return nil
|
||||
}
|
||||
|
||||
state := appstate.HashState{Version: version, Hash: hash}
|
||||
|
||||
hasMore := true
|
||||
wantSnapshot := fullSync
|
||||
for hasMore {
|
||||
patches, err := cli.fetchAppStatePatches(name, state.Version, wantSnapshot)
|
||||
wantSnapshot = false
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch app state %s patches: %w", name, err)
|
||||
}
|
||||
hasMore = patches.HasMorePatches
|
||||
|
||||
mutations, newState, err := cli.appStateProc.DecodePatches(patches, state, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decode app state %s patches: %w", name, err)
|
||||
}
|
||||
state = newState
|
||||
for _, mutation := range mutations {
|
||||
cli.dispatchAppState(mutation, !fullSync || cli.EmitAppStateEventsOnFullSync)
|
||||
}
|
||||
}
|
||||
if fullSync {
|
||||
cli.Log.Debugf("Full sync of app state %s completed. Current version: %d", name, state.Version)
|
||||
cli.dispatchEvent(&events.AppStateSyncComplete{Name: name})
|
||||
} else {
|
||||
cli.Log.Debugf("Synced app state %s from version %d to %d", name, version, state.Version)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (cli *Client) dispatchAppState(mutation appstate.Mutation, dispatchEvts bool) {
|
||||
if mutation.Operation != waProto.SyncdMutation_SET {
|
||||
return
|
||||
}
|
||||
|
||||
if dispatchEvts {
|
||||
cli.dispatchEvent(&events.AppState{Index: mutation.Index, SyncActionValue: mutation.Action})
|
||||
}
|
||||
|
||||
var jid types.JID
|
||||
if len(mutation.Index) > 1 {
|
||||
jid, _ = types.ParseJID(mutation.Index[1])
|
||||
}
|
||||
ts := time.Unix(mutation.Action.GetTimestamp(), 0)
|
||||
|
||||
var storeUpdateError error
|
||||
var eventToDispatch interface{}
|
||||
switch mutation.Index[0] {
|
||||
case "mute":
|
||||
act := mutation.Action.GetMuteAction()
|
||||
eventToDispatch = &events.Mute{JID: jid, Timestamp: ts, Action: act}
|
||||
var mutedUntil time.Time
|
||||
if act.GetMuted() {
|
||||
mutedUntil = time.Unix(act.GetMuteEndTimestamp(), 0)
|
||||
}
|
||||
if cli.Store.ChatSettings != nil {
|
||||
storeUpdateError = cli.Store.ChatSettings.PutMutedUntil(jid, mutedUntil)
|
||||
}
|
||||
case "pin_v1":
|
||||
act := mutation.Action.GetPinAction()
|
||||
eventToDispatch = &events.Pin{JID: jid, Timestamp: ts, Action: act}
|
||||
if cli.Store.ChatSettings != nil {
|
||||
storeUpdateError = cli.Store.ChatSettings.PutPinned(jid, act.GetPinned())
|
||||
}
|
||||
case "archive":
|
||||
act := mutation.Action.GetArchiveChatAction()
|
||||
eventToDispatch = &events.Archive{JID: jid, Timestamp: ts, Action: act}
|
||||
if cli.Store.ChatSettings != nil {
|
||||
storeUpdateError = cli.Store.ChatSettings.PutArchived(jid, act.GetArchived())
|
||||
}
|
||||
case "contact":
|
||||
act := mutation.Action.GetContactAction()
|
||||
eventToDispatch = &events.Contact{JID: jid, Timestamp: ts, Action: act}
|
||||
if cli.Store.Contacts != nil {
|
||||
storeUpdateError = cli.Store.Contacts.PutContactName(jid, act.GetFirstName(), act.GetFullName())
|
||||
}
|
||||
case "star":
|
||||
if len(mutation.Index) < 5 {
|
||||
return
|
||||
}
|
||||
evt := events.Star{
|
||||
ChatJID: jid,
|
||||
MessageID: mutation.Index[2],
|
||||
Timestamp: ts,
|
||||
Action: mutation.Action.GetStarAction(),
|
||||
IsFromMe: mutation.Index[3] == "1",
|
||||
}
|
||||
if mutation.Index[4] != "0" {
|
||||
evt.SenderJID, _ = types.ParseJID(mutation.Index[4])
|
||||
}
|
||||
eventToDispatch = &evt
|
||||
case "deleteMessageForMe":
|
||||
if len(mutation.Index) < 5 {
|
||||
return
|
||||
}
|
||||
evt := events.DeleteForMe{
|
||||
ChatJID: jid,
|
||||
MessageID: mutation.Index[2],
|
||||
Timestamp: ts,
|
||||
Action: mutation.Action.GetDeleteMessageForMeAction(),
|
||||
IsFromMe: mutation.Index[3] == "1",
|
||||
}
|
||||
if mutation.Index[4] != "0" {
|
||||
evt.SenderJID, _ = types.ParseJID(mutation.Index[4])
|
||||
}
|
||||
eventToDispatch = &evt
|
||||
case "setting_pushName":
|
||||
eventToDispatch = &events.PushNameSetting{Timestamp: ts, Action: mutation.Action.GetPushNameSetting()}
|
||||
cli.Store.PushName = mutation.Action.GetPushNameSetting().GetName()
|
||||
err := cli.Store.Save()
|
||||
if err != nil {
|
||||
cli.Log.Errorf("Failed to save device store after updating push name: %v", err)
|
||||
}
|
||||
case "setting_unarchiveChats":
|
||||
eventToDispatch = &events.UnarchiveChatsSetting{Timestamp: ts, Action: mutation.Action.GetUnarchiveChatsSetting()}
|
||||
}
|
||||
if storeUpdateError != nil {
|
||||
cli.Log.Errorf("Failed to update device store after app state mutation: %v", storeUpdateError)
|
||||
}
|
||||
if dispatchEvts && eventToDispatch != nil {
|
||||
cli.dispatchEvent(eventToDispatch)
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *Client) downloadExternalAppStateBlob(ref *waProto.ExternalBlobReference) ([]byte, error) {
|
||||
return cli.Download(ref)
|
||||
}
|
||||
|
||||
func (cli *Client) fetchAppStatePatches(name appstate.WAPatchName, fromVersion uint64, snapshot bool) (*appstate.PatchList, error) {
|
||||
attrs := waBinary.Attrs{
|
||||
"name": string(name),
|
||||
"return_snapshot": snapshot,
|
||||
}
|
||||
if !snapshot {
|
||||
attrs["version"] = fromVersion
|
||||
}
|
||||
resp, err := cli.sendIQ(infoQuery{
|
||||
Namespace: "w:sync:app:state",
|
||||
Type: "set",
|
||||
To: types.ServerJID,
|
||||
Content: []waBinary.Node{{
|
||||
Tag: "sync",
|
||||
Content: []waBinary.Node{{
|
||||
Tag: "collection",
|
||||
Attrs: attrs,
|
||||
}},
|
||||
}},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return appstate.ParsePatchList(resp, cli.downloadExternalAppStateBlob)
|
||||
}
|
||||
310
vendor/go.mau.fi/whatsmeow/appstate/decode.go
vendored
Normal file
310
vendor/go.mau.fi/whatsmeow/appstate/decode.go
vendored
Normal file
@@ -0,0 +1,310 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package appstate
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
waBinary "go.mau.fi/whatsmeow/binary"
|
||||
waProto "go.mau.fi/whatsmeow/binary/proto"
|
||||
"go.mau.fi/whatsmeow/store"
|
||||
"go.mau.fi/whatsmeow/util/cbcutil"
|
||||
)
|
||||
|
||||
// PatchList represents a decoded response to getting app state patches from the WhatsApp servers.
|
||||
type PatchList struct {
|
||||
Name WAPatchName
|
||||
HasMorePatches bool
|
||||
Patches []*waProto.SyncdPatch
|
||||
Snapshot *waProto.SyncdSnapshot
|
||||
}
|
||||
|
||||
// DownloadExternalFunc is a function that can download a blob of external app state patches.
|
||||
type DownloadExternalFunc func(*waProto.ExternalBlobReference) ([]byte, error)
|
||||
|
||||
func parseSnapshotInternal(collection *waBinary.Node, downloadExternal DownloadExternalFunc) (*waProto.SyncdSnapshot, error) {
|
||||
snapshotNode := collection.GetChildByTag("snapshot")
|
||||
rawSnapshot, ok := snapshotNode.Content.([]byte)
|
||||
if snapshotNode.Tag != "snapshot" || !ok {
|
||||
return nil, nil
|
||||
}
|
||||
var snapshot waProto.ExternalBlobReference
|
||||
err := proto.Unmarshal(rawSnapshot, &snapshot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal snapshot: %w", err)
|
||||
}
|
||||
var rawData []byte
|
||||
rawData, err = downloadExternal(&snapshot)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to download external mutations: %w", err)
|
||||
}
|
||||
var downloaded waProto.SyncdSnapshot
|
||||
err = proto.Unmarshal(rawData, &downloaded)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal mutation list: %w", err)
|
||||
}
|
||||
return &downloaded, nil
|
||||
}
|
||||
|
||||
func parsePatchListInternal(collection *waBinary.Node, downloadExternal DownloadExternalFunc) ([]*waProto.SyncdPatch, error) {
|
||||
patchesNode := collection.GetChildByTag("patches")
|
||||
patchNodes := patchesNode.GetChildren()
|
||||
patches := make([]*waProto.SyncdPatch, 0, len(patchNodes))
|
||||
for i, patchNode := range patchNodes {
|
||||
rawPatch, ok := patchNode.Content.([]byte)
|
||||
if patchNode.Tag != "patch" || !ok {
|
||||
continue
|
||||
}
|
||||
var patch waProto.SyncdPatch
|
||||
err := proto.Unmarshal(rawPatch, &patch)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal patch #%d: %w", i+1, err)
|
||||
}
|
||||
if patch.GetExternalMutations() != nil && downloadExternal != nil {
|
||||
var rawData []byte
|
||||
rawData, err = downloadExternal(patch.GetExternalMutations())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to download external mutations: %w", err)
|
||||
}
|
||||
var downloaded waProto.SyncdMutations
|
||||
err = proto.Unmarshal(rawData, &downloaded)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to unmarshal mutation list: %w", err)
|
||||
} else if len(downloaded.GetMutations()) == 0 {
|
||||
return nil, fmt.Errorf("didn't get any mutations from download")
|
||||
}
|
||||
patch.Mutations = downloaded.Mutations
|
||||
}
|
||||
patches = append(patches, &patch)
|
||||
}
|
||||
return patches, nil
|
||||
}
|
||||
|
||||
// ParsePatchList will decode an XML node containing app state patches, including downloading any external blobs.
|
||||
func ParsePatchList(node *waBinary.Node, downloadExternal DownloadExternalFunc) (*PatchList, error) {
|
||||
collection := node.GetChildByTag("sync", "collection")
|
||||
ag := collection.AttrGetter()
|
||||
snapshot, err := parseSnapshotInternal(&collection, downloadExternal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
patches, err := parsePatchListInternal(&collection, downloadExternal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
list := &PatchList{
|
||||
Name: WAPatchName(ag.String("name")),
|
||||
HasMorePatches: ag.OptionalBool("has_more_patches"),
|
||||
Patches: patches,
|
||||
Snapshot: snapshot,
|
||||
}
|
||||
return list, ag.Error()
|
||||
}
|
||||
|
||||
type patchOutput struct {
|
||||
RemovedMACs [][]byte
|
||||
AddedMACs []store.AppStateMutationMAC
|
||||
Mutations []Mutation
|
||||
}
|
||||
|
||||
func (proc *Processor) decodeMutations(mutations []*waProto.SyncdMutation, out *patchOutput, validateMACs bool) error {
|
||||
for i, mutation := range mutations {
|
||||
keyID := mutation.GetRecord().GetKeyId().GetId()
|
||||
keys, err := proc.getAppStateKey(keyID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get key %X to decode mutation: %w", keyID, err)
|
||||
}
|
||||
content := mutation.GetRecord().GetValue().GetBlob()
|
||||
content, valueMAC := content[:len(content)-32], content[len(content)-32:]
|
||||
if validateMACs {
|
||||
expectedValueMAC := generateContentMAC(mutation.GetOperation(), content, keyID, keys.ValueMAC)
|
||||
if !bytes.Equal(expectedValueMAC, valueMAC) {
|
||||
return fmt.Errorf("failed to verify mutation #%d: %w", i+1, ErrMismatchingContentMAC)
|
||||
}
|
||||
}
|
||||
iv, content := content[:16], content[16:]
|
||||
plaintext, err := cbcutil.Decrypt(keys.ValueEncryption, iv, content)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to decrypt mutation #%d: %w", i+1, err)
|
||||
}
|
||||
var syncAction waProto.SyncActionData
|
||||
err = proto.Unmarshal(plaintext, &syncAction)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal mutation #%d: %w", i+1, err)
|
||||
}
|
||||
indexMAC := mutation.GetRecord().GetIndex().GetBlob()
|
||||
if validateMACs {
|
||||
expectedIndexMAC := concatAndHMAC(sha256.New, keys.Index, syncAction.Index)
|
||||
if !bytes.Equal(expectedIndexMAC, indexMAC) {
|
||||
return fmt.Errorf("failed to verify mutation #%d: %w", i+1, ErrMismatchingIndexMAC)
|
||||
}
|
||||
}
|
||||
var index []string
|
||||
err = json.Unmarshal(syncAction.GetIndex(), &index)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to unmarshal index of mutation #%d: %w", i+1, err)
|
||||
}
|
||||
if mutation.GetOperation() == waProto.SyncdMutation_REMOVE {
|
||||
out.RemovedMACs = append(out.RemovedMACs, indexMAC)
|
||||
} else if mutation.GetOperation() == waProto.SyncdMutation_SET {
|
||||
out.AddedMACs = append(out.AddedMACs, store.AppStateMutationMAC{
|
||||
IndexMAC: indexMAC,
|
||||
ValueMAC: valueMAC,
|
||||
})
|
||||
}
|
||||
out.Mutations = append(out.Mutations, Mutation{
|
||||
Operation: mutation.GetOperation(),
|
||||
Action: syncAction.GetValue(),
|
||||
Index: index,
|
||||
IndexMAC: indexMAC,
|
||||
ValueMAC: valueMAC,
|
||||
})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (proc *Processor) storeMACs(name WAPatchName, currentState HashState, out *patchOutput) {
|
||||
err := proc.Store.AppState.PutAppStateVersion(string(name), currentState.Version, currentState.Hash)
|
||||
if err != nil {
|
||||
proc.Log.Errorf("Failed to update app state version in the database: %v", err)
|
||||
}
|
||||
err = proc.Store.AppState.DeleteAppStateMutationMACs(string(name), out.RemovedMACs)
|
||||
if err != nil {
|
||||
proc.Log.Errorf("Failed to remove deleted mutation MACs from the database: %v", err)
|
||||
}
|
||||
err = proc.Store.AppState.PutAppStateMutationMACs(string(name), currentState.Version, out.AddedMACs)
|
||||
if err != nil {
|
||||
proc.Log.Errorf("Failed to insert added mutation MACs to the database: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (proc *Processor) validateSnapshotMAC(name WAPatchName, currentState HashState, keyID, expectedSnapshotMAC []byte) (keys ExpandedAppStateKeys, err error) {
|
||||
keys, err = proc.getAppStateKey(keyID)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to get key %X to verify patch v%d MACs: %w", keyID, currentState.Version, err)
|
||||
return
|
||||
}
|
||||
snapshotMAC := currentState.generateSnapshotMAC(name, keys.SnapshotMAC)
|
||||
if !bytes.Equal(snapshotMAC, expectedSnapshotMAC) {
|
||||
err = fmt.Errorf("failed to verify patch v%d: %w", currentState.Version, ErrMismatchingLTHash)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (proc *Processor) decodeSnapshot(name WAPatchName, ss *waProto.SyncdSnapshot, initialState HashState, validateMACs bool, newMutationsInput []Mutation) (newMutations []Mutation, currentState HashState, err error) {
|
||||
currentState = initialState
|
||||
currentState.Version = ss.GetVersion().GetVersion()
|
||||
|
||||
encryptedMutations := make([]*waProto.SyncdMutation, len(ss.GetRecords()))
|
||||
for i, record := range ss.GetRecords() {
|
||||
encryptedMutations[i] = &waProto.SyncdMutation{
|
||||
Operation: waProto.SyncdMutation_SET.Enum(),
|
||||
Record: record,
|
||||
}
|
||||
}
|
||||
|
||||
var warn []error
|
||||
warn, err = currentState.updateHash(encryptedMutations, func(indexMAC []byte, maxIndex int) ([]byte, error) {
|
||||
return nil, nil
|
||||
})
|
||||
if len(warn) > 0 {
|
||||
proc.Log.Warnf("Warnings while updating hash for %s: %+v", name, warn)
|
||||
}
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to update state hash: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
if validateMACs {
|
||||
_, err = proc.validateSnapshotMAC(name, currentState, ss.GetKeyId().GetId(), ss.GetMac())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var out patchOutput
|
||||
out.Mutations = newMutationsInput
|
||||
err = proc.decodeMutations(encryptedMutations, &out, validateMACs)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to decode snapshot of v%d: %w", currentState.Version, err)
|
||||
return
|
||||
}
|
||||
proc.storeMACs(name, currentState, &out)
|
||||
newMutations = out.Mutations
|
||||
return
|
||||
}
|
||||
|
||||
// DecodePatches will decode all the patches in a PatchList into a list of app state mutations.
|
||||
func (proc *Processor) DecodePatches(list *PatchList, initialState HashState, validateMACs bool) (newMutations []Mutation, currentState HashState, err error) {
|
||||
currentState = initialState
|
||||
var expectedLength int
|
||||
if list.Snapshot != nil {
|
||||
expectedLength = len(list.Snapshot.GetRecords())
|
||||
}
|
||||
for _, patch := range list.Patches {
|
||||
expectedLength += len(patch.GetMutations())
|
||||
}
|
||||
newMutations = make([]Mutation, 0, expectedLength)
|
||||
|
||||
if list.Snapshot != nil {
|
||||
newMutations, currentState, err = proc.decodeSnapshot(list.Name, list.Snapshot, currentState, validateMACs, newMutations)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for _, patch := range list.Patches {
|
||||
version := patch.GetVersion().GetVersion()
|
||||
currentState.Version = version
|
||||
var warn []error
|
||||
warn, err = currentState.updateHash(patch.GetMutations(), func(indexMAC []byte, maxIndex int) ([]byte, error) {
|
||||
for i := maxIndex - 1; i >= 0; i-- {
|
||||
if bytes.Equal(patch.Mutations[i].GetRecord().GetIndex().GetBlob(), indexMAC) {
|
||||
value := patch.Mutations[i].GetRecord().GetValue().GetBlob()
|
||||
return value[len(value)-32:], nil
|
||||
}
|
||||
}
|
||||
// Previous value not found in current patch, look in the database
|
||||
return proc.Store.AppState.GetAppStateMutationMAC(string(list.Name), indexMAC)
|
||||
})
|
||||
if len(warn) > 0 {
|
||||
proc.Log.Warnf("Warnings while updating hash for %s: %+v", list.Name, warn)
|
||||
}
|
||||
if err != nil {
|
||||
err = fmt.Errorf("failed to update state hash: %w", err)
|
||||
return
|
||||
}
|
||||
|
||||
if validateMACs {
|
||||
var keys ExpandedAppStateKeys
|
||||
keys, err = proc.validateSnapshotMAC(list.Name, currentState, patch.GetKeyId().GetId(), patch.GetSnapshotMac())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
patchMAC := generatePatchMAC(patch, list.Name, keys.PatchMAC)
|
||||
if !bytes.Equal(patchMAC, patch.GetPatchMac()) {
|
||||
err = fmt.Errorf("failed to verify patch v%d: %w", version, ErrMismatchingPatchMAC)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var out patchOutput
|
||||
out.Mutations = newMutations
|
||||
err = proc.decodeMutations(patch.GetMutations(), &out, validateMACs)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
proc.storeMACs(list.Name, currentState, &out)
|
||||
newMutations = out.Mutations
|
||||
}
|
||||
return
|
||||
}
|
||||
19
vendor/go.mau.fi/whatsmeow/appstate/errors.go
vendored
Normal file
19
vendor/go.mau.fi/whatsmeow/appstate/errors.go
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package appstate
|
||||
|
||||
import "errors"
|
||||
|
||||
// Errors that this package can return.
|
||||
var (
|
||||
ErrMissingPreviousSetValueOperation = errors.New("missing value MAC of previous SET operation")
|
||||
ErrMismatchingLTHash = errors.New("mismatching LTHash")
|
||||
ErrMismatchingPatchMAC = errors.New("mismatching patch MAC")
|
||||
ErrMismatchingContentMAC = errors.New("mismatching content MAC")
|
||||
ErrMismatchingIndexMAC = errors.New("mismatching index MAC")
|
||||
ErrKeyNotFound = errors.New("didn't find app state key")
|
||||
)
|
||||
96
vendor/go.mau.fi/whatsmeow/appstate/hash.go
vendored
Normal file
96
vendor/go.mau.fi/whatsmeow/appstate/hash.go
vendored
Normal file
@@ -0,0 +1,96 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package appstate
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"hash"
|
||||
|
||||
"go.mau.fi/whatsmeow/appstate/lthash"
|
||||
waProto "go.mau.fi/whatsmeow/binary/proto"
|
||||
)
|
||||
|
||||
type Mutation struct {
|
||||
Operation waProto.SyncdMutation_SyncdMutationSyncdOperation
|
||||
Action *waProto.SyncActionValue
|
||||
Index []string
|
||||
IndexMAC []byte
|
||||
ValueMAC []byte
|
||||
}
|
||||
|
||||
type HashState struct {
|
||||
Version uint64
|
||||
Hash [128]byte
|
||||
}
|
||||
|
||||
func (hs *HashState) updateHash(mutations []*waProto.SyncdMutation, getPrevSetValueMAC func(indexMAC []byte, maxIndex int) ([]byte, error)) ([]error, error) {
|
||||
var added, removed [][]byte
|
||||
var warnings []error
|
||||
|
||||
for i, mutation := range mutations {
|
||||
if mutation.GetOperation() == waProto.SyncdMutation_SET {
|
||||
value := mutation.GetRecord().GetValue().GetBlob()
|
||||
added = append(added, value[len(value)-32:])
|
||||
}
|
||||
indexMAC := mutation.GetRecord().GetIndex().GetBlob()
|
||||
removal, err := getPrevSetValueMAC(indexMAC, i)
|
||||
if err != nil {
|
||||
return warnings, fmt.Errorf("failed to get value MAC of previous SET operation: %w", err)
|
||||
} else if removal != nil {
|
||||
removed = append(removed, removal)
|
||||
} else if mutation.GetOperation() == waProto.SyncdMutation_REMOVE {
|
||||
// TODO figure out if there are certain cases that are safe to ignore and others that aren't
|
||||
// At least removing contact access from WhatsApp seems to create a REMOVE op for your own JID
|
||||
// that points to a non-existent index and is safe to ignore here. Other keys might not be safe to ignore.
|
||||
warnings = append(warnings, fmt.Errorf("%w for %X", ErrMissingPreviousSetValueOperation, indexMAC))
|
||||
//return ErrMissingPreviousSetValueOperation
|
||||
}
|
||||
}
|
||||
|
||||
lthash.WAPatchIntegrity.SubtractThenAddInPlace(hs.Hash[:], removed, added)
|
||||
return warnings, nil
|
||||
}
|
||||
|
||||
func uint64ToBytes(val uint64) []byte {
|
||||
data := make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(data, val)
|
||||
return data
|
||||
}
|
||||
|
||||
func concatAndHMAC(alg func() hash.Hash, key []byte, data ...[]byte) []byte {
|
||||
h := hmac.New(alg, key)
|
||||
for _, item := range data {
|
||||
h.Write(item)
|
||||
}
|
||||
return h.Sum(nil)
|
||||
}
|
||||
|
||||
func (hs *HashState) generateSnapshotMAC(name WAPatchName, key []byte) []byte {
|
||||
return concatAndHMAC(sha256.New, key, hs.Hash[:], uint64ToBytes(hs.Version), []byte(name))
|
||||
}
|
||||
|
||||
func generatePatchMAC(patch *waProto.SyncdPatch, name WAPatchName, key []byte) []byte {
|
||||
dataToHash := make([][]byte, len(patch.GetMutations())+3)
|
||||
dataToHash[0] = patch.GetSnapshotMac()
|
||||
for i, mutation := range patch.Mutations {
|
||||
val := mutation.GetRecord().GetValue().GetBlob()
|
||||
dataToHash[i+1] = val[len(val)-32:]
|
||||
}
|
||||
dataToHash[len(dataToHash)-2] = uint64ToBytes(patch.GetVersion().GetVersion())
|
||||
dataToHash[len(dataToHash)-1] = []byte(name)
|
||||
return concatAndHMAC(sha256.New, key, dataToHash...)
|
||||
}
|
||||
|
||||
func generateContentMAC(operation waProto.SyncdMutation_SyncdMutationSyncdOperation, data, keyID, key []byte) []byte {
|
||||
operationBytes := []byte{byte(operation) + 1}
|
||||
keyDataLength := uint64ToBytes(uint64(len(keyID) + 1))
|
||||
return concatAndHMAC(sha512.New, key, operationBytes, keyID, data, keyDataLength)[:32]
|
||||
}
|
||||
85
vendor/go.mau.fi/whatsmeow/appstate/keys.go
vendored
Normal file
85
vendor/go.mau.fi/whatsmeow/appstate/keys.go
vendored
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Package appstate implements encoding and decoding WhatsApp's app state patches.
|
||||
package appstate
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"sync"
|
||||
|
||||
"go.mau.fi/whatsmeow/store"
|
||||
"go.mau.fi/whatsmeow/util/hkdfutil"
|
||||
waLog "go.mau.fi/whatsmeow/util/log"
|
||||
)
|
||||
|
||||
// WAPatchName represents a type of app state patch.
|
||||
type WAPatchName string
|
||||
|
||||
const (
|
||||
// WAPatchCriticalBlock contains the user's settings like push name and locale.
|
||||
WAPatchCriticalBlock WAPatchName = "critical_block"
|
||||
// WAPatchCriticalUnblockLow contains the user's contact list.
|
||||
WAPatchCriticalUnblockLow WAPatchName = "critical_unblock_low"
|
||||
// WAPatchRegularLow contains some local chat settings like pin, archive status, and the setting of whether to unarchive chats when messages come in.
|
||||
WAPatchRegularLow WAPatchName = "regular_low"
|
||||
// WAPatchRegularHigh contains more local chat settings like mute status and starred messages.
|
||||
WAPatchRegularHigh WAPatchName = "regular_high"
|
||||
// WAPatchRegular contains protocol info about app state patches like key expiration.
|
||||
WAPatchRegular WAPatchName = "regular"
|
||||
)
|
||||
|
||||
// AllPatchNames contains all currently known patch state names.
|
||||
var AllPatchNames = [...]WAPatchName{WAPatchCriticalBlock, WAPatchCriticalUnblockLow, WAPatchRegularHigh, WAPatchRegular, WAPatchRegularLow}
|
||||
|
||||
type Processor struct {
|
||||
keyCache map[string]ExpandedAppStateKeys
|
||||
keyCacheLock sync.Mutex
|
||||
Store *store.Device
|
||||
Log waLog.Logger
|
||||
}
|
||||
|
||||
func NewProcessor(store *store.Device, log waLog.Logger) *Processor {
|
||||
return &Processor{
|
||||
keyCache: make(map[string]ExpandedAppStateKeys),
|
||||
Store: store,
|
||||
Log: log,
|
||||
}
|
||||
}
|
||||
|
||||
type ExpandedAppStateKeys struct {
|
||||
Index []byte
|
||||
ValueEncryption []byte
|
||||
ValueMAC []byte
|
||||
SnapshotMAC []byte
|
||||
PatchMAC []byte
|
||||
}
|
||||
|
||||
func expandAppStateKeys(keyData []byte) (keys ExpandedAppStateKeys) {
|
||||
appStateKeyExpanded := hkdfutil.SHA256(keyData, nil, []byte("WhatsApp Mutation Keys"), 160)
|
||||
return ExpandedAppStateKeys{appStateKeyExpanded[0:32], appStateKeyExpanded[32:64], appStateKeyExpanded[64:96], appStateKeyExpanded[96:128], appStateKeyExpanded[128:160]}
|
||||
}
|
||||
|
||||
func (proc *Processor) getAppStateKey(keyID []byte) (keys ExpandedAppStateKeys, err error) {
|
||||
keyCacheID := base64.RawStdEncoding.EncodeToString(keyID)
|
||||
var ok bool
|
||||
|
||||
proc.keyCacheLock.Lock()
|
||||
defer proc.keyCacheLock.Unlock()
|
||||
|
||||
keys, ok = proc.keyCache[keyCacheID]
|
||||
if !ok {
|
||||
var keyData *store.AppStateSyncKey
|
||||
keyData, err = proc.Store.AppStateKeys.GetAppStateSyncKey(keyID)
|
||||
if keyData != nil {
|
||||
keys = expandAppStateKeys(keyData.Data)
|
||||
proc.keyCache[keyCacheID] = keys
|
||||
} else if err == nil {
|
||||
err = ErrKeyNotFound
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
58
vendor/go.mau.fi/whatsmeow/appstate/lthash/lthash.go
vendored
Normal file
58
vendor/go.mau.fi/whatsmeow/appstate/lthash/lthash.go
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Package lthash implements a summation based hash algorithm that maintains the
|
||||
// integrity of a piece of data over a series of mutations. You can add/remove
|
||||
// mutations, and it'll return a hash equal to if the same series of mutations
|
||||
// was made sequentially.
|
||||
package lthash
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"go.mau.fi/whatsmeow/util/hkdfutil"
|
||||
)
|
||||
|
||||
type LTHash struct {
|
||||
HKDFInfo []byte
|
||||
HKDFSize uint8
|
||||
}
|
||||
|
||||
// WAPatchIntegrity is a LTHash instance initialized with the details used for verifying integrity of WhatsApp app state sync patches.
|
||||
var WAPatchIntegrity = LTHash{[]byte("WhatsApp Patch Integrity"), 128}
|
||||
|
||||
func (lth LTHash) SubtractThenAdd(base []byte, subtract, add [][]byte) []byte {
|
||||
output := make([]byte, len(base))
|
||||
copy(output, base)
|
||||
lth.SubtractThenAddInPlace(output, subtract, add)
|
||||
return output
|
||||
}
|
||||
|
||||
func (lth LTHash) SubtractThenAddInPlace(base []byte, subtract, add [][]byte) {
|
||||
lth.multipleOp(base, subtract, true)
|
||||
lth.multipleOp(base, add, false)
|
||||
}
|
||||
|
||||
func (lth LTHash) multipleOp(base []byte, input [][]byte, subtract bool) {
|
||||
for _, item := range input {
|
||||
performPointwiseWithOverflow(base, hkdfutil.SHA256(item, nil, lth.HKDFInfo, lth.HKDFSize), subtract)
|
||||
}
|
||||
}
|
||||
|
||||
func performPointwiseWithOverflow(base, input []byte, subtract bool) []byte {
|
||||
for i := 0; i < len(base); i += 2 {
|
||||
x := binary.LittleEndian.Uint16(base[i : i+2])
|
||||
y := binary.LittleEndian.Uint16(input[i : i+2])
|
||||
var result uint16
|
||||
if subtract {
|
||||
result = x - y
|
||||
} else {
|
||||
result = x + y
|
||||
}
|
||||
binary.LittleEndian.PutUint16(base[i:i+2], result)
|
||||
}
|
||||
return base
|
||||
}
|
||||
177
vendor/go.mau.fi/whatsmeow/binary/attrs.go
vendored
Normal file
177
vendor/go.mau.fi/whatsmeow/binary/attrs.go
vendored
Normal file
@@ -0,0 +1,177 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package binary
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
|
||||
"go.mau.fi/whatsmeow/types"
|
||||
)
|
||||
|
||||
// AttrUtility is a helper struct for reading multiple XML attributes and checking for errors afterwards.
|
||||
//
|
||||
// The functions return values directly and append any decoding errors to the Errors slice. The
|
||||
// slice can then be checked after all necessary attributes are read, instead of having to check
|
||||
// each attribute for errors separately.
|
||||
type AttrUtility struct {
|
||||
Attrs Attrs
|
||||
Errors []error
|
||||
}
|
||||
|
||||
// AttrGetter returns the AttrUtility for this Node.
|
||||
func (n *Node) AttrGetter() *AttrUtility {
|
||||
return &AttrUtility{Attrs: n.Attrs, Errors: make([]error, 0)}
|
||||
}
|
||||
|
||||
func (au *AttrUtility) GetJID(key string, require bool) (jidVal types.JID, ok bool) {
|
||||
var val interface{}
|
||||
if val, ok = au.Attrs[key]; !ok {
|
||||
if require {
|
||||
au.Errors = append(au.Errors, fmt.Errorf("didn't find required JID attribute '%s'", key))
|
||||
}
|
||||
} else if jidVal, ok = val.(types.JID); !ok {
|
||||
au.Errors = append(au.Errors, fmt.Errorf("expected attribute '%s' to be JID, but was %T", key, val))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// OptionalJID returns the JID under the given key. If there's no valid JID under the given key, this will return nil.
|
||||
// However, if the attribute is completely missing, this will not store an error.
|
||||
func (au *AttrUtility) OptionalJID(key string) *types.JID {
|
||||
jid, ok := au.GetJID(key, false)
|
||||
if ok {
|
||||
return &jid
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// OptionalJIDOrEmpty returns the JID under the given key. If there's no valid JID under the given key, this will return an empty JID.
|
||||
// However, if the attribute is completely missing, this will not store an error.
|
||||
func (au *AttrUtility) OptionalJIDOrEmpty(key string) types.JID {
|
||||
jid, ok := au.GetJID(key, false)
|
||||
if ok {
|
||||
return jid
|
||||
}
|
||||
return types.EmptyJID
|
||||
}
|
||||
|
||||
// JID returns the JID under the given key.
|
||||
// If there's no valid JID under the given key, an error will be stored and a blank JID struct will be returned.
|
||||
func (au *AttrUtility) JID(key string) types.JID {
|
||||
jid, _ := au.GetJID(key, true)
|
||||
return jid
|
||||
}
|
||||
|
||||
func (au *AttrUtility) GetString(key string, require bool) (strVal string, ok bool) {
|
||||
var val interface{}
|
||||
if val, ok = au.Attrs[key]; !ok {
|
||||
if require {
|
||||
au.Errors = append(au.Errors, fmt.Errorf("didn't find required attribute '%s'", key))
|
||||
}
|
||||
} else if strVal, ok = val.(string); !ok {
|
||||
au.Errors = append(au.Errors, fmt.Errorf("expected attribute '%s' to be string, but was %T", key, val))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (au *AttrUtility) GetInt64(key string, require bool) (int64, bool) {
|
||||
if strVal, ok := au.GetString(key, require); !ok {
|
||||
return 0, false
|
||||
} else if intVal, err := strconv.ParseInt(strVal, 10, 64); err != nil {
|
||||
au.Errors = append(au.Errors, fmt.Errorf("failed to parse int in attribute '%s': %w", key, err))
|
||||
return 0, false
|
||||
} else {
|
||||
return intVal, true
|
||||
}
|
||||
}
|
||||
|
||||
func (au *AttrUtility) GetUint64(key string, require bool) (uint64, bool) {
|
||||
if strVal, ok := au.GetString(key, require); !ok {
|
||||
return 0, false
|
||||
} else if intVal, err := strconv.ParseUint(strVal, 10, 64); err != nil {
|
||||
au.Errors = append(au.Errors, fmt.Errorf("failed to parse uint in attribute '%s': %w", key, err))
|
||||
return 0, false
|
||||
} else {
|
||||
return intVal, true
|
||||
}
|
||||
}
|
||||
|
||||
func (au *AttrUtility) GetBool(key string, require bool) (bool, bool) {
|
||||
if strVal, ok := au.GetString(key, require); !ok {
|
||||
return false, false
|
||||
} else if boolVal, err := strconv.ParseBool(strVal); err != nil {
|
||||
au.Errors = append(au.Errors, fmt.Errorf("failed to parse bool in attribute '%s': %w", key, err))
|
||||
return false, false
|
||||
} else {
|
||||
return boolVal, true
|
||||
}
|
||||
}
|
||||
|
||||
// OptionalString returns the string under the given key.
|
||||
func (au *AttrUtility) OptionalString(key string) string {
|
||||
strVal, _ := au.GetString(key, false)
|
||||
return strVal
|
||||
}
|
||||
|
||||
// String returns the string under the given key.
|
||||
// If there's no valid string under the given key, an error will be stored and an empty string will be returned.
|
||||
func (au *AttrUtility) String(key string) string {
|
||||
strVal, _ := au.GetString(key, true)
|
||||
return strVal
|
||||
}
|
||||
|
||||
func (au *AttrUtility) OptionalInt(key string) int {
|
||||
val, _ := au.GetInt64(key, false)
|
||||
return int(val)
|
||||
}
|
||||
|
||||
func (au *AttrUtility) Int(key string) int {
|
||||
val, _ := au.GetInt64(key, true)
|
||||
return int(val)
|
||||
}
|
||||
|
||||
func (au *AttrUtility) Int64(key string) int64 {
|
||||
val, _ := au.GetInt64(key, true)
|
||||
return val
|
||||
}
|
||||
|
||||
func (au *AttrUtility) Uint64(key string) uint64 {
|
||||
val, _ := au.GetUint64(key, true)
|
||||
return val
|
||||
}
|
||||
|
||||
func (au *AttrUtility) OptionalBool(key string) bool {
|
||||
val, _ := au.GetBool(key, false)
|
||||
return val
|
||||
}
|
||||
|
||||
func (au *AttrUtility) Bool(key string) bool {
|
||||
val, _ := au.GetBool(key, true)
|
||||
return val
|
||||
}
|
||||
|
||||
// OK returns true if there are no errors.
|
||||
func (au *AttrUtility) OK() bool {
|
||||
return len(au.Errors) == 0
|
||||
}
|
||||
|
||||
// Error returns the list of errors as a single error interface, or nil if there are no errors.
|
||||
func (au *AttrUtility) Error() error {
|
||||
if au.OK() {
|
||||
return nil
|
||||
}
|
||||
return ErrorList(au.Errors)
|
||||
}
|
||||
|
||||
// ErrorList is a list of errors that implements the error interface itself.
|
||||
type ErrorList []error
|
||||
|
||||
// Error returns all the errors in the list as a string.
|
||||
func (el ErrorList) Error() string {
|
||||
return fmt.Sprintf("%+v", []error(el))
|
||||
}
|
||||
353
vendor/go.mau.fi/whatsmeow/binary/decoder.go
vendored
Normal file
353
vendor/go.mau.fi/whatsmeow/binary/decoder.go
vendored
Normal file
@@ -0,0 +1,353 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"go.mau.fi/whatsmeow/binary/token"
|
||||
"go.mau.fi/whatsmeow/types"
|
||||
)
|
||||
|
||||
type binaryDecoder struct {
|
||||
data []byte
|
||||
index int
|
||||
}
|
||||
|
||||
func newDecoder(data []byte) *binaryDecoder {
|
||||
return &binaryDecoder{data, 0}
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) checkEOS(length int) error {
|
||||
if r.index+length > len(r.data) {
|
||||
return io.EOF
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readByte() (byte, error) {
|
||||
if err := r.checkEOS(1); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
b := r.data[r.index]
|
||||
r.index++
|
||||
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readIntN(n int, littleEndian bool) (int, error) {
|
||||
if err := r.checkEOS(n); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
var ret int
|
||||
|
||||
for i := 0; i < n; i++ {
|
||||
var curShift int
|
||||
if littleEndian {
|
||||
curShift = i
|
||||
} else {
|
||||
curShift = n - i - 1
|
||||
}
|
||||
ret |= int(r.data[r.index+i]) << uint(curShift*8)
|
||||
}
|
||||
|
||||
r.index += n
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readInt8(littleEndian bool) (int, error) {
|
||||
return r.readIntN(1, littleEndian)
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readInt16(littleEndian bool) (int, error) {
|
||||
return r.readIntN(2, littleEndian)
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readInt20() (int, error) {
|
||||
if err := r.checkEOS(3); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
ret := ((int(r.data[r.index]) & 15) << 16) + (int(r.data[r.index+1]) << 8) + int(r.data[r.index+2])
|
||||
r.index += 3
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readInt32(littleEndian bool) (int, error) {
|
||||
return r.readIntN(4, littleEndian)
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readPacked8(tag int) (string, error) {
|
||||
startByte, err := r.readByte()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var build strings.Builder
|
||||
|
||||
for i := 0; i < int(startByte&127); i++ {
|
||||
currByte, err := r.readByte()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
lower, err := unpackByte(tag, currByte&0xF0>>4)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
upper, err := unpackByte(tag, currByte&0x0F)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
build.WriteByte(lower)
|
||||
build.WriteByte(upper)
|
||||
}
|
||||
|
||||
ret := build.String()
|
||||
if startByte>>7 != 0 {
|
||||
ret = ret[:len(ret)-1]
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func unpackByte(tag int, value byte) (byte, error) {
|
||||
switch tag {
|
||||
case token.Nibble8:
|
||||
return unpackNibble(value)
|
||||
case token.Hex8:
|
||||
return unpackHex(value)
|
||||
default:
|
||||
return 0, fmt.Errorf("unpackByte with unknown tag %d", tag)
|
||||
}
|
||||
}
|
||||
|
||||
func unpackNibble(value byte) (byte, error) {
|
||||
switch {
|
||||
case value < 10:
|
||||
return '0' + value, nil
|
||||
case value == 10:
|
||||
return '-', nil
|
||||
case value == 11:
|
||||
return '.', nil
|
||||
case value == 15:
|
||||
return 0, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("unpackNibble with value %d", value)
|
||||
}
|
||||
}
|
||||
|
||||
func unpackHex(value byte) (byte, error) {
|
||||
switch {
|
||||
case value < 10:
|
||||
return '0' + value, nil
|
||||
case value < 16:
|
||||
return 'A' + value - 10, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("unpackHex with value %d", value)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readListSize(tag int) (int, error) {
|
||||
switch tag {
|
||||
case token.ListEmpty:
|
||||
return 0, nil
|
||||
case token.List8:
|
||||
return r.readInt8(false)
|
||||
case token.List16:
|
||||
return r.readInt16(false)
|
||||
default:
|
||||
return 0, fmt.Errorf("readListSize with unknown tag %d at position %d", tag, r.index)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) read(string bool) (interface{}, error) {
|
||||
tagByte, err := r.readByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tag := int(tagByte)
|
||||
switch tag {
|
||||
case token.ListEmpty:
|
||||
return nil, nil
|
||||
case token.List8, token.List16:
|
||||
return r.readList(tag)
|
||||
case token.Binary8:
|
||||
size, err := r.readInt8(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.readBytesOrString(size, string)
|
||||
case token.Binary20:
|
||||
size, err := r.readInt20()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.readBytesOrString(size, string)
|
||||
case token.Binary32:
|
||||
size, err := r.readInt32(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return r.readBytesOrString(size, string)
|
||||
case token.Dictionary0, token.Dictionary1, token.Dictionary2, token.Dictionary3:
|
||||
i, err := r.readInt8(false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return token.GetDoubleToken(tag-token.Dictionary0, i)
|
||||
case token.JIDPair:
|
||||
return r.readJIDPair()
|
||||
case token.ADJID:
|
||||
return r.readADJID()
|
||||
case token.Nibble8, token.Hex8:
|
||||
return r.readPacked8(tag)
|
||||
default:
|
||||
if tag >= 1 && tag < len(token.SingleByteTokens) {
|
||||
return token.SingleByteTokens[tag], nil
|
||||
}
|
||||
return "", fmt.Errorf("%w %d at position %d", ErrInvalidToken, tag, r.index)
|
||||
}
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readJIDPair() (interface{}, error) {
|
||||
user, err := r.read(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
server, err := r.read(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if server == nil {
|
||||
return nil, ErrInvalidJIDType
|
||||
} else if user == nil {
|
||||
return types.NewJID("", server.(string)), nil
|
||||
}
|
||||
return types.NewJID(user.(string), server.(string)), nil
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readADJID() (interface{}, error) {
|
||||
agent, err := r.readByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
device, err := r.readByte()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user, err := r.read(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return types.NewADJID(user.(string), agent, device), nil
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readAttributes(n int) (Attrs, error) {
|
||||
if n == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
ret := make(Attrs)
|
||||
for i := 0; i < n; i++ {
|
||||
keyIfc, err := r.read(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key, ok := keyIfc.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("%[1]w at position %[3]d (%[2]T): %+[2]v", ErrNonStringKey, key, r.index)
|
||||
}
|
||||
|
||||
ret[key], err = r.read(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readList(tag int) ([]Node, error) {
|
||||
size, err := r.readListSize(tag)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := make([]Node, size)
|
||||
for i := 0; i < size; i++ {
|
||||
n, err := r.readNode()
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret[i] = *n
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readNode() (*Node, error) {
|
||||
ret := &Node{}
|
||||
|
||||
size, err := r.readInt8(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
listSize, err := r.readListSize(size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rawDesc, err := r.read(true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ret.Tag = rawDesc.(string)
|
||||
if listSize == 0 || ret.Tag == "" {
|
||||
return nil, ErrInvalidNode
|
||||
}
|
||||
|
||||
ret.Attrs, err = r.readAttributes((listSize - 1) >> 1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if listSize%2 == 1 {
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
ret.Content, err = r.read(false)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readBytesOrString(length int, asString bool) (interface{}, error) {
|
||||
data, err := r.readRaw(length)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if asString {
|
||||
return string(data), nil
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (r *binaryDecoder) readRaw(length int) ([]byte, error) {
|
||||
if err := r.checkEOS(length); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := r.data[r.index : r.index+length]
|
||||
r.index += length
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
293
vendor/go.mau.fi/whatsmeow/binary/encoder.go
vendored
Normal file
293
vendor/go.mau.fi/whatsmeow/binary/encoder.go
vendored
Normal file
@@ -0,0 +1,293 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
|
||||
"go.mau.fi/whatsmeow/binary/token"
|
||||
"go.mau.fi/whatsmeow/types"
|
||||
)
|
||||
|
||||
type binaryEncoder struct {
|
||||
data []byte
|
||||
}
|
||||
|
||||
func newEncoder() *binaryEncoder {
|
||||
return &binaryEncoder{[]byte{0}}
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) getData() []byte {
|
||||
return w.data
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) pushByte(b byte) {
|
||||
w.data = append(w.data, b)
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) pushBytes(bytes []byte) {
|
||||
w.data = append(w.data, bytes...)
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) pushIntN(value, n int, littleEndian bool) {
|
||||
for i := 0; i < n; i++ {
|
||||
var curShift int
|
||||
if littleEndian {
|
||||
curShift = i
|
||||
} else {
|
||||
curShift = n - i - 1
|
||||
}
|
||||
w.pushByte(byte((value >> uint(curShift*8)) & 0xFF))
|
||||
}
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) pushInt20(value int) {
|
||||
w.pushBytes([]byte{byte((value >> 16) & 0x0F), byte((value >> 8) & 0xFF), byte(value & 0xFF)})
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) pushInt8(value int) {
|
||||
w.pushIntN(value, 1, false)
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) pushInt16(value int) {
|
||||
w.pushIntN(value, 2, false)
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) pushInt32(value int) {
|
||||
w.pushIntN(value, 4, false)
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) pushString(value string) {
|
||||
w.pushBytes([]byte(value))
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) writeByteLength(length int) {
|
||||
if length < 256 {
|
||||
w.pushByte(token.Binary8)
|
||||
w.pushInt8(length)
|
||||
} else if length < (1 << 20) {
|
||||
w.pushByte(token.Binary20)
|
||||
w.pushInt20(length)
|
||||
} else if length < math.MaxInt32 {
|
||||
w.pushByte(token.Binary32)
|
||||
w.pushInt32(length)
|
||||
} else {
|
||||
panic(fmt.Errorf("length is too large: %d", length))
|
||||
}
|
||||
}
|
||||
|
||||
const tagSize = 1
|
||||
|
||||
func (w *binaryEncoder) writeNode(n Node) {
|
||||
if n.Tag == "0" {
|
||||
w.pushByte(token.List8)
|
||||
w.pushByte(token.ListEmpty)
|
||||
return
|
||||
}
|
||||
|
||||
hasContent := 0
|
||||
if n.Content != nil {
|
||||
hasContent = 1
|
||||
}
|
||||
|
||||
w.writeListStart(2*len(n.Attrs) + tagSize + hasContent)
|
||||
w.writeString(n.Tag)
|
||||
w.writeAttributes(n.Attrs)
|
||||
if n.Content != nil {
|
||||
w.write(n.Content)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) write(data interface{}) {
|
||||
switch typedData := data.(type) {
|
||||
case nil:
|
||||
w.pushByte(token.ListEmpty)
|
||||
case types.JID:
|
||||
w.writeJID(typedData)
|
||||
case string:
|
||||
w.writeString(typedData)
|
||||
case int:
|
||||
w.writeString(strconv.Itoa(typedData))
|
||||
case int32:
|
||||
w.writeString(strconv.FormatInt(int64(typedData), 10))
|
||||
case uint:
|
||||
w.writeString(strconv.FormatUint(uint64(typedData), 10))
|
||||
case uint32:
|
||||
w.writeString(strconv.FormatUint(uint64(typedData), 10))
|
||||
case int64:
|
||||
w.writeString(strconv.FormatInt(typedData, 10))
|
||||
case uint64:
|
||||
w.writeString(strconv.FormatUint(typedData, 10))
|
||||
case bool:
|
||||
w.writeString(strconv.FormatBool(typedData))
|
||||
case []byte:
|
||||
w.writeBytes(typedData)
|
||||
case []Node:
|
||||
w.writeListStart(len(typedData))
|
||||
for _, n := range typedData {
|
||||
w.writeNode(n)
|
||||
}
|
||||
default:
|
||||
panic(fmt.Errorf("%w: %T", ErrInvalidType, typedData))
|
||||
}
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) writeString(data string) {
|
||||
var dictIndex byte
|
||||
if tokenIndex, ok := token.IndexOfSingleToken(data); ok {
|
||||
w.pushByte(tokenIndex)
|
||||
} else if dictIndex, tokenIndex, ok = token.IndexOfDoubleByteToken(data); ok {
|
||||
w.pushByte(token.Dictionary0 + dictIndex)
|
||||
w.pushByte(tokenIndex)
|
||||
} else if validateNibble(data) {
|
||||
w.writePackedBytes(data, token.Nibble8)
|
||||
} else if validateHex(data) {
|
||||
w.writePackedBytes(data, token.Hex8)
|
||||
} else {
|
||||
w.writeStringRaw(data)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) writeBytes(value []byte) {
|
||||
w.writeByteLength(len(value))
|
||||
w.pushBytes(value)
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) writeStringRaw(value string) {
|
||||
w.writeByteLength(len(value))
|
||||
w.pushString(value)
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) writeJID(jid types.JID) {
|
||||
if jid.AD {
|
||||
w.pushByte(token.ADJID)
|
||||
w.pushByte(jid.Agent)
|
||||
w.pushByte(jid.Device)
|
||||
w.writeString(jid.User)
|
||||
} else {
|
||||
w.pushByte(token.JIDPair)
|
||||
if len(jid.User) == 0 {
|
||||
w.pushByte(token.ListEmpty)
|
||||
} else {
|
||||
w.write(jid.User)
|
||||
}
|
||||
w.write(jid.Server)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) writeAttributes(attributes Attrs) {
|
||||
if attributes == nil {
|
||||
return
|
||||
}
|
||||
|
||||
for key, val := range attributes {
|
||||
if val == "" || val == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
w.writeString(key)
|
||||
w.write(val)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) writeListStart(listSize int) {
|
||||
if listSize == 0 {
|
||||
w.pushByte(byte(token.ListEmpty))
|
||||
} else if listSize < 256 {
|
||||
w.pushByte(byte(token.List8))
|
||||
w.pushInt8(listSize)
|
||||
} else {
|
||||
w.pushByte(byte(token.List16))
|
||||
w.pushInt16(listSize)
|
||||
}
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) writePackedBytes(value string, dataType int) {
|
||||
if len(value) > token.PackedMax {
|
||||
panic(fmt.Errorf("too many bytes to pack: %d", len(value)))
|
||||
}
|
||||
|
||||
w.pushByte(byte(dataType))
|
||||
|
||||
roundedLength := byte(math.Ceil(float64(len(value)) / 2.0))
|
||||
if len(value)%2 != 0 {
|
||||
roundedLength |= 128
|
||||
}
|
||||
w.pushByte(roundedLength)
|
||||
var packer func(byte) byte
|
||||
if dataType == token.Nibble8 {
|
||||
packer = packNibble
|
||||
} else if dataType == token.Hex8 {
|
||||
packer = packHex
|
||||
} else {
|
||||
// This should only be called with the correct values
|
||||
panic(fmt.Errorf("invalid packed byte data type %v", dataType))
|
||||
}
|
||||
for i, l := 0, len(value)/2; i < l; i++ {
|
||||
w.pushByte(w.packBytePair(packer, value[2*i], value[2*i+1]))
|
||||
}
|
||||
if len(value)%2 != 0 {
|
||||
w.pushByte(w.packBytePair(packer, value[len(value)-1], '\x00'))
|
||||
}
|
||||
}
|
||||
|
||||
func (w *binaryEncoder) packBytePair(packer func(byte) byte, part1, part2 byte) byte {
|
||||
return (packer(part1) << 4) | packer(part2)
|
||||
}
|
||||
|
||||
func validateNibble(value string) bool {
|
||||
if len(value) > token.PackedMax {
|
||||
return false
|
||||
}
|
||||
for _, char := range value {
|
||||
if !(char >= '0' && char <= '9') && char != '-' && char != '.' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func packNibble(value byte) byte {
|
||||
switch value {
|
||||
case '-':
|
||||
return 10
|
||||
case '.':
|
||||
return 11
|
||||
case 0:
|
||||
return 15
|
||||
default:
|
||||
if value >= '0' && value <= '9' {
|
||||
return value - '0'
|
||||
}
|
||||
// This should be validated beforehand
|
||||
panic(fmt.Errorf("invalid string to pack as nibble: %d / '%s'", value, string(value)))
|
||||
}
|
||||
}
|
||||
|
||||
func validateHex(value string) bool {
|
||||
if len(value) > token.PackedMax {
|
||||
return false
|
||||
}
|
||||
for _, char := range value {
|
||||
if !(char >= '0' && char <= '9') && !(char >= 'A' && char <= 'F') && !(char >= 'a' && char <= 'f') {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func packHex(value byte) byte {
|
||||
switch {
|
||||
case value >= '0' && value <= '9':
|
||||
return value - '0'
|
||||
case value >= 'A' && value <= 'F':
|
||||
return 10 + value - 'A'
|
||||
case value >= 'a' && value <= 'f':
|
||||
return 10 + value - 'a'
|
||||
case value == 0:
|
||||
return 15
|
||||
default:
|
||||
// This should be validated beforehand
|
||||
panic(fmt.Errorf("invalid string to pack as hex: %d / '%s'", value, string(value)))
|
||||
}
|
||||
}
|
||||
12
vendor/go.mau.fi/whatsmeow/binary/errors.go
vendored
Normal file
12
vendor/go.mau.fi/whatsmeow/binary/errors.go
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
package binary
|
||||
|
||||
import "errors"
|
||||
|
||||
// Errors returned by the binary XML decoder.
|
||||
var (
|
||||
ErrInvalidType = errors.New("unsupported payload type")
|
||||
ErrInvalidJIDType = errors.New("invalid JID type")
|
||||
ErrInvalidNode = errors.New("invalid node")
|
||||
ErrInvalidToken = errors.New("invalid token with tag")
|
||||
ErrNonStringKey = errors.New("non-string key")
|
||||
)
|
||||
83
vendor/go.mau.fi/whatsmeow/binary/node.go
vendored
Normal file
83
vendor/go.mau.fi/whatsmeow/binary/node.go
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Package binary implements encoding and decoding documents in WhatsApp's binary XML format.
|
||||
package binary
|
||||
|
||||
// Attrs is a type alias for the attributes of an XML element (Node).
|
||||
type Attrs = map[string]interface{}
|
||||
|
||||
// Node represents an XML element.
|
||||
type Node struct {
|
||||
Tag string // The tag of the element.
|
||||
Attrs Attrs // The attributes of the element.
|
||||
Content interface{} // The content inside the element. Can be nil, a list of Nodes, or a byte array.
|
||||
}
|
||||
|
||||
// GetChildren returns the Content of the node as a list of nodes. If the content is not a list of nodes, this returns nil.
|
||||
func (n *Node) GetChildren() []Node {
|
||||
if n.Content == nil {
|
||||
return nil
|
||||
}
|
||||
children, ok := n.Content.([]Node)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return children
|
||||
}
|
||||
|
||||
// GetChildrenByTag returns the same list as GetChildren, but filters it by tag first.
|
||||
func (n *Node) GetChildrenByTag(tag string) (children []Node) {
|
||||
for _, node := range n.GetChildren() {
|
||||
if node.Tag == tag {
|
||||
children = append(children, node)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// GetOptionalChildByTag finds the first child with the given tag and returns it.
|
||||
// Each provided tag will recurse in, so this is useful for getting a specific nested element.
|
||||
func (n *Node) GetOptionalChildByTag(tags ...string) (val Node, ok bool) {
|
||||
val = *n
|
||||
Outer:
|
||||
for _, tag := range tags {
|
||||
for _, child := range val.GetChildren() {
|
||||
if child.Tag == tag {
|
||||
val = child
|
||||
continue Outer
|
||||
}
|
||||
}
|
||||
// If no matching children are found, return false
|
||||
return
|
||||
}
|
||||
// All iterations of loop found a matching child, return it
|
||||
ok = true
|
||||
return
|
||||
}
|
||||
|
||||
// GetChildByTag does the same thing as GetOptionalChildByTag, but returns the Node directly without the ok boolean.
|
||||
func (n *Node) GetChildByTag(tags ...string) Node {
|
||||
node, _ := n.GetOptionalChildByTag(tags...)
|
||||
return node
|
||||
}
|
||||
|
||||
// Marshal encodes an XML element (Node) into WhatsApp's binary XML representation.
|
||||
func Marshal(n Node) ([]byte, error) {
|
||||
w := newEncoder()
|
||||
w.writeNode(n)
|
||||
return w.getData(), nil
|
||||
}
|
||||
|
||||
// Unmarshal decodes WhatsApp's binary XML representation into a Node.
|
||||
func Unmarshal(data []byte) (*Node, error) {
|
||||
r := newDecoder(data)
|
||||
n, err := r.readNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
21520
vendor/go.mau.fi/whatsmeow/binary/proto/def.pb.go
vendored
Normal file
21520
vendor/go.mau.fi/whatsmeow/binary/proto/def.pb.go
vendored
Normal file
File diff suppressed because it is too large
Load Diff
BIN
vendor/go.mau.fi/whatsmeow/binary/proto/def.pb.raw
vendored
Normal file
BIN
vendor/go.mau.fi/whatsmeow/binary/proto/def.pb.raw
vendored
Normal file
Binary file not shown.
2011
vendor/go.mau.fi/whatsmeow/binary/proto/def.proto
vendored
Normal file
2011
vendor/go.mau.fi/whatsmeow/binary/proto/def.proto
vendored
Normal file
File diff suppressed because it is too large
Load Diff
2
vendor/go.mau.fi/whatsmeow/binary/proto/doc.go
vendored
Normal file
2
vendor/go.mau.fi/whatsmeow/binary/proto/doc.go
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package proto contains the compiled protobuf structs from WhatsApp's protobuf schema.
|
||||
package proto
|
||||
88
vendor/go.mau.fi/whatsmeow/binary/token/token.go
vendored
Normal file
88
vendor/go.mau.fi/whatsmeow/binary/token/token.go
vendored
Normal file
File diff suppressed because one or more lines are too long
31
vendor/go.mau.fi/whatsmeow/binary/unpack.go
vendored
Normal file
31
vendor/go.mau.fi/whatsmeow/binary/unpack.go
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package binary
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/zlib"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Unpack unpacks the given decrypted data from the WhatsApp web API.
|
||||
//
|
||||
// It checks the first byte to decide whether to uncompress the data with zlib or just return as-is
|
||||
// (without the first byte). There's currently no corresponding Pack function because Marshal
|
||||
// already returns the data with a leading zero (i.e. not compressed).
|
||||
func Unpack(data []byte) ([]byte, error) {
|
||||
dataType, data := data[0], data[1:]
|
||||
if 2&dataType > 0 {
|
||||
if decompressor, err := zlib.NewReader(bytes.NewReader(data)); err != nil {
|
||||
return nil, fmt.Errorf("failed to create zlib reader: %w", err)
|
||||
} else if data, err = io.ReadAll(decompressor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return data, nil
|
||||
}
|
||||
108
vendor/go.mau.fi/whatsmeow/binary/xml.go
vendored
Normal file
108
vendor/go.mau.fi/whatsmeow/binary/xml.go
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package binary
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Options to control how Node.XMLString behaves.
|
||||
var (
|
||||
IndentXML = false
|
||||
MaxBytesToPrintAsHex = 128
|
||||
)
|
||||
|
||||
// XMLString converts the Node to its XML representation
|
||||
func (n *Node) XMLString() string {
|
||||
content := n.contentString()
|
||||
if len(content) == 0 {
|
||||
return fmt.Sprintf("<%[1]s%[2]s/>", n.Tag, n.attributeString())
|
||||
}
|
||||
newline := "\n"
|
||||
if len(content) == 1 || !IndentXML {
|
||||
newline = ""
|
||||
}
|
||||
return fmt.Sprintf("<%[1]s%[2]s>%[4]s%[3]s%[4]s</%[1]s>", n.Tag, n.attributeString(), strings.Join(content, newline), newline)
|
||||
}
|
||||
|
||||
func (n *Node) attributeString() string {
|
||||
if len(n.Attrs) == 0 {
|
||||
return ""
|
||||
}
|
||||
stringAttrs := make([]string, len(n.Attrs)+1)
|
||||
i := 1
|
||||
for key, value := range n.Attrs {
|
||||
stringAttrs[i] = fmt.Sprintf(`%s="%v"`, key, value)
|
||||
i++
|
||||
}
|
||||
sort.Strings(stringAttrs)
|
||||
return strings.Join(stringAttrs, " ")
|
||||
}
|
||||
|
||||
func printable(data []byte) string {
|
||||
if !utf8.Valid(data) {
|
||||
return ""
|
||||
}
|
||||
str := string(data)
|
||||
for _, c := range str {
|
||||
if !unicode.IsPrint(c) {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
func (n *Node) contentString() []string {
|
||||
split := make([]string, 0)
|
||||
switch content := n.Content.(type) {
|
||||
case []Node:
|
||||
for _, item := range content {
|
||||
split = append(split, strings.Split(item.XMLString(), "\n")...)
|
||||
}
|
||||
case []byte:
|
||||
if strContent := printable(content); len(strContent) > 0 {
|
||||
if IndentXML {
|
||||
split = append(split, strings.Split(string(content), "\n")...)
|
||||
} else {
|
||||
split = append(split, strings.ReplaceAll(string(content), "\n", "\\n"))
|
||||
}
|
||||
} else if len(content) > MaxBytesToPrintAsHex {
|
||||
split = append(split, fmt.Sprintf("<!-- %d bytes -->", len(content)))
|
||||
} else if !IndentXML {
|
||||
split = append(split, hex.EncodeToString(content))
|
||||
} else {
|
||||
hexData := hex.EncodeToString(content)
|
||||
for i := 0; i < len(hexData); i += 80 {
|
||||
if len(hexData) < i+80 {
|
||||
split = append(split, hexData[i:])
|
||||
} else {
|
||||
split = append(split, hexData[i:i+80])
|
||||
}
|
||||
}
|
||||
}
|
||||
case nil:
|
||||
// don't append anything
|
||||
default:
|
||||
strContent := fmt.Sprintf("%s", content)
|
||||
if IndentXML {
|
||||
split = append(split, strings.Split(strContent, "\n")...)
|
||||
} else {
|
||||
split = append(split, strings.ReplaceAll(strContent, "\n", "\\n"))
|
||||
}
|
||||
}
|
||||
if len(split) > 1 && IndentXML {
|
||||
for i, line := range split {
|
||||
split[i] = " " + line
|
||||
}
|
||||
}
|
||||
return split
|
||||
}
|
||||
73
vendor/go.mau.fi/whatsmeow/call.go
vendored
Normal file
73
vendor/go.mau.fi/whatsmeow/call.go
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package whatsmeow
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
waBinary "go.mau.fi/whatsmeow/binary"
|
||||
"go.mau.fi/whatsmeow/types"
|
||||
"go.mau.fi/whatsmeow/types/events"
|
||||
)
|
||||
|
||||
func (cli *Client) handleCallEvent(node *waBinary.Node) {
|
||||
go cli.sendAck(node)
|
||||
|
||||
if len(node.GetChildren()) != 1 {
|
||||
cli.dispatchEvent(&events.UnknownCallEvent{Node: node})
|
||||
return
|
||||
}
|
||||
ag := node.AttrGetter()
|
||||
child := node.GetChildren()[0]
|
||||
cag := child.AttrGetter()
|
||||
basicMeta := types.BasicCallMeta{
|
||||
From: ag.JID("from"),
|
||||
Timestamp: time.Unix(ag.Int64("t"), 0),
|
||||
CallCreator: cag.JID("call-creator"),
|
||||
CallID: cag.String("call-id"),
|
||||
}
|
||||
switch child.Tag {
|
||||
case "offer":
|
||||
cli.dispatchEvent(&events.CallOffer{
|
||||
BasicCallMeta: basicMeta,
|
||||
CallRemoteMeta: types.CallRemoteMeta{
|
||||
RemotePlatform: ag.String("platform"),
|
||||
RemoteVersion: ag.String("version"),
|
||||
},
|
||||
Data: &child,
|
||||
})
|
||||
case "offer_notice":
|
||||
cli.dispatchEvent(&events.CallOfferNotice{
|
||||
BasicCallMeta: basicMeta,
|
||||
Media: cag.String("media"),
|
||||
Type: cag.String("type"),
|
||||
Data: &child,
|
||||
})
|
||||
case "relaylatency":
|
||||
cli.dispatchEvent(&events.CallRelayLatency{
|
||||
BasicCallMeta: basicMeta,
|
||||
Data: &child,
|
||||
})
|
||||
case "accept":
|
||||
cli.dispatchEvent(&events.CallAccept{
|
||||
BasicCallMeta: basicMeta,
|
||||
CallRemoteMeta: types.CallRemoteMeta{
|
||||
RemotePlatform: ag.String("platform"),
|
||||
RemoteVersion: ag.String("version"),
|
||||
},
|
||||
Data: &child,
|
||||
})
|
||||
case "terminate":
|
||||
cli.dispatchEvent(&events.CallTerminate{
|
||||
BasicCallMeta: basicMeta,
|
||||
Reason: cag.String("reason"),
|
||||
Data: &child,
|
||||
})
|
||||
default:
|
||||
cli.dispatchEvent(&events.UnknownCallEvent{Node: node})
|
||||
}
|
||||
}
|
||||
459
vendor/go.mau.fi/whatsmeow/client.go
vendored
Normal file
459
vendor/go.mau.fi/whatsmeow/client.go
vendored
Normal file
@@ -0,0 +1,459 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
// Package whatsmeow implements a client for interacting with the WhatsApp web multidevice API.
|
||||
package whatsmeow
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"go.mau.fi/whatsmeow/appstate"
|
||||
waBinary "go.mau.fi/whatsmeow/binary"
|
||||
waProto "go.mau.fi/whatsmeow/binary/proto"
|
||||
"go.mau.fi/whatsmeow/socket"
|
||||
"go.mau.fi/whatsmeow/store"
|
||||
"go.mau.fi/whatsmeow/types"
|
||||
"go.mau.fi/whatsmeow/types/events"
|
||||
"go.mau.fi/whatsmeow/util/keys"
|
||||
waLog "go.mau.fi/whatsmeow/util/log"
|
||||
)
|
||||
|
||||
// EventHandler is a function that can handle events from WhatsApp.
|
||||
type EventHandler func(evt interface{})
|
||||
type nodeHandler func(node *waBinary.Node)
|
||||
|
||||
var nextHandlerID uint32
|
||||
|
||||
type wrappedEventHandler struct {
|
||||
fn EventHandler
|
||||
id uint32
|
||||
}
|
||||
|
||||
// Client contains everything necessary to connect to and interact with the WhatsApp web API.
|
||||
type Client struct {
|
||||
Store *store.Device
|
||||
Log waLog.Logger
|
||||
recvLog waLog.Logger
|
||||
sendLog waLog.Logger
|
||||
|
||||
socket *socket.NoiseSocket
|
||||
socketLock sync.RWMutex
|
||||
|
||||
isLoggedIn uint32
|
||||
expectedDisconnectVal uint32
|
||||
EnableAutoReconnect bool
|
||||
LastSuccessfulConnect time.Time
|
||||
AutoReconnectErrors int
|
||||
|
||||
// EmitAppStateEventsOnFullSync can be set to true if you want to get app state events emitted
|
||||
// even when re-syncing the whole state.
|
||||
EmitAppStateEventsOnFullSync bool
|
||||
|
||||
appStateProc *appstate.Processor
|
||||
appStateSyncLock sync.Mutex
|
||||
|
||||
uploadPreKeysLock sync.Mutex
|
||||
lastPreKeyUpload time.Time
|
||||
|
||||
mediaConn *MediaConn
|
||||
mediaConnLock sync.Mutex
|
||||
|
||||
responseWaiters map[string]chan<- *waBinary.Node
|
||||
responseWaitersLock sync.Mutex
|
||||
|
||||
nodeHandlers map[string]nodeHandler
|
||||
handlerQueue chan *waBinary.Node
|
||||
eventHandlers []wrappedEventHandler
|
||||
eventHandlersLock sync.RWMutex
|
||||
|
||||
messageRetries map[string]int
|
||||
messageRetriesLock sync.Mutex
|
||||
|
||||
privacySettingsCache atomic.Value
|
||||
|
||||
groupParticipantsCache map[types.JID][]types.JID
|
||||
groupParticipantsCacheLock sync.Mutex
|
||||
userDevicesCache map[types.JID][]types.JID
|
||||
userDevicesCacheLock sync.Mutex
|
||||
|
||||
recentMessagesMap map[recentMessageKey]*waProto.Message
|
||||
recentMessagesList [recentMessagesSize]recentMessageKey
|
||||
recentMessagesPtr int
|
||||
recentMessagesLock sync.RWMutex
|
||||
// GetMessageForRetry is used to find the source message for handling retry receipts
|
||||
// when the message is not found in the recently sent message cache.
|
||||
GetMessageForRetry func(to types.JID, id types.MessageID) *waProto.Message
|
||||
// PreRetryCallback is called before a retry receipt is accepted.
|
||||
// If it returns false, the accepting will be cancelled and the retry receipt will be ignored.
|
||||
PreRetryCallback func(receipt *events.Receipt, retryCount int, msg *waProto.Message) bool
|
||||
|
||||
uniqueID string
|
||||
idCounter uint32
|
||||
}
|
||||
|
||||
// Size of buffer for the channel that all incoming XML nodes go through.
|
||||
// In general it shouldn't go past a few buffered messages, but the channel is big to be safe.
|
||||
const handlerQueueSize = 2048
|
||||
|
||||
// NewClient initializes a new WhatsApp web client.
|
||||
//
|
||||
// The logger can be nil, it will default to a no-op logger.
|
||||
//
|
||||
// The device store must be set. A default SQL-backed implementation is available in the store/sqlstore package.
|
||||
// container, err := sqlstore.New("sqlite3", "file:yoursqlitefile.db?_foreign_keys=on", nil)
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// // If you want multiple sessions, remember their JIDs and use .GetDevice(jid) or .GetAllDevices() instead.
|
||||
// deviceStore, err := container.GetFirstDevice()
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// client := whatsmeow.NewClient(deviceStore, nil)
|
||||
func NewClient(deviceStore *store.Device, log waLog.Logger) *Client {
|
||||
if log == nil {
|
||||
log = waLog.Noop
|
||||
}
|
||||
randomBytes := make([]byte, 2)
|
||||
_, _ = rand.Read(randomBytes)
|
||||
cli := &Client{
|
||||
Store: deviceStore,
|
||||
Log: log,
|
||||
recvLog: log.Sub("Recv"),
|
||||
sendLog: log.Sub("Send"),
|
||||
uniqueID: fmt.Sprintf("%d.%d-", randomBytes[0], randomBytes[1]),
|
||||
responseWaiters: make(map[string]chan<- *waBinary.Node),
|
||||
eventHandlers: make([]wrappedEventHandler, 0, 1),
|
||||
messageRetries: make(map[string]int),
|
||||
handlerQueue: make(chan *waBinary.Node, handlerQueueSize),
|
||||
appStateProc: appstate.NewProcessor(deviceStore, log.Sub("AppState")),
|
||||
|
||||
groupParticipantsCache: make(map[types.JID][]types.JID),
|
||||
userDevicesCache: make(map[types.JID][]types.JID),
|
||||
|
||||
recentMessagesMap: make(map[recentMessageKey]*waProto.Message, recentMessagesSize),
|
||||
GetMessageForRetry: func(to types.JID, id types.MessageID) *waProto.Message { return nil },
|
||||
|
||||
EnableAutoReconnect: true,
|
||||
}
|
||||
cli.nodeHandlers = map[string]nodeHandler{
|
||||
"message": cli.handleEncryptedMessage,
|
||||
"receipt": cli.handleReceipt,
|
||||
"call": cli.handleCallEvent,
|
||||
"chatstate": cli.handleChatState,
|
||||
"presence": cli.handlePresence,
|
||||
"notification": cli.handleNotification,
|
||||
"success": cli.handleConnectSuccess,
|
||||
"failure": cli.handleConnectFailure,
|
||||
"stream:error": cli.handleStreamError,
|
||||
"iq": cli.handleIQ,
|
||||
"ib": cli.handleIB,
|
||||
}
|
||||
return cli
|
||||
}
|
||||
|
||||
// Connect connects the client to the WhatsApp web websocket. After connection, it will either
|
||||
// authenticate if there's data in the device store, or emit a QREvent to set up a new link.
|
||||
func (cli *Client) Connect() error {
|
||||
cli.socketLock.Lock()
|
||||
defer cli.socketLock.Unlock()
|
||||
if cli.socket != nil {
|
||||
if !cli.socket.IsConnected() {
|
||||
cli.unlockedDisconnect()
|
||||
} else {
|
||||
return ErrAlreadyConnected
|
||||
}
|
||||
}
|
||||
|
||||
cli.resetExpectedDisconnect()
|
||||
fs := socket.NewFrameSocket(cli.Log.Sub("Socket"), socket.WAConnHeader)
|
||||
if err := fs.Connect(); err != nil {
|
||||
fs.Close(0)
|
||||
return err
|
||||
} else if err = cli.doHandshake(fs, *keys.NewKeyPair()); err != nil {
|
||||
fs.Close(0)
|
||||
return fmt.Errorf("noise handshake failed: %w", err)
|
||||
}
|
||||
go cli.keepAliveLoop(cli.socket.Context())
|
||||
go cli.handlerQueueLoop(cli.socket.Context())
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsLoggedIn returns true after the client is successfully connected and authenticated on WhatsApp.
|
||||
func (cli *Client) IsLoggedIn() bool {
|
||||
return atomic.LoadUint32(&cli.isLoggedIn) == 1
|
||||
}
|
||||
|
||||
func (cli *Client) onDisconnect(ns *socket.NoiseSocket, remote bool) {
|
||||
ns.Stop(false)
|
||||
cli.socketLock.Lock()
|
||||
defer cli.socketLock.Unlock()
|
||||
if cli.socket == ns {
|
||||
cli.socket = nil
|
||||
cli.clearResponseWaiters()
|
||||
if !cli.isExpectedDisconnect() && remote {
|
||||
cli.Log.Debugf("Emitting Disconnected event")
|
||||
go cli.dispatchEvent(&events.Disconnected{})
|
||||
go cli.autoReconnect()
|
||||
} else if remote {
|
||||
cli.Log.Debugf("OnDisconnect() called, but it was expected, so not emitting event")
|
||||
} else {
|
||||
cli.Log.Debugf("OnDisconnect() called after manual disconnection")
|
||||
}
|
||||
} else {
|
||||
cli.Log.Debugf("Ignoring OnDisconnect on different socket")
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *Client) expectDisconnect() {
|
||||
atomic.StoreUint32(&cli.expectedDisconnectVal, 1)
|
||||
}
|
||||
|
||||
func (cli *Client) resetExpectedDisconnect() {
|
||||
atomic.StoreUint32(&cli.expectedDisconnectVal, 0)
|
||||
}
|
||||
|
||||
func (cli *Client) isExpectedDisconnect() bool {
|
||||
return atomic.LoadUint32(&cli.expectedDisconnectVal) == 1
|
||||
}
|
||||
|
||||
func (cli *Client) autoReconnect() {
|
||||
if !cli.EnableAutoReconnect || cli.Store.ID == nil {
|
||||
return
|
||||
}
|
||||
for {
|
||||
cli.AutoReconnectErrors++
|
||||
autoReconnectDelay := time.Duration(cli.AutoReconnectErrors) * 2 * time.Second
|
||||
cli.Log.Debugf("Automatically reconnecting after %v", autoReconnectDelay)
|
||||
time.Sleep(autoReconnectDelay)
|
||||
err := cli.Connect()
|
||||
if errors.Is(err, ErrAlreadyConnected) {
|
||||
cli.Log.Debugf("Connect() said we're already connected after autoreconnect sleep")
|
||||
return
|
||||
} else if err != nil {
|
||||
cli.Log.Errorf("Error reconnecting after autoreconnect sleep: %v", err)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IsConnected checks if the client is connected to the WhatsApp web websocket.
|
||||
// Note that this doesn't check if the client is authenticated. See the IsLoggedIn field for that.
|
||||
func (cli *Client) IsConnected() bool {
|
||||
cli.socketLock.RLock()
|
||||
connected := cli.socket != nil && cli.socket.IsConnected()
|
||||
cli.socketLock.RUnlock()
|
||||
return connected
|
||||
}
|
||||
|
||||
// Disconnect disconnects from the WhatsApp web websocket.
|
||||
func (cli *Client) Disconnect() {
|
||||
if cli.socket == nil {
|
||||
return
|
||||
}
|
||||
cli.socketLock.Lock()
|
||||
cli.unlockedDisconnect()
|
||||
cli.socketLock.Unlock()
|
||||
}
|
||||
|
||||
// Disconnect closes the websocket connection.
|
||||
func (cli *Client) unlockedDisconnect() {
|
||||
if cli.socket != nil {
|
||||
cli.socket.Stop(true)
|
||||
cli.socket = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Logout sends a request to unlink the device, then disconnects from the websocket and deletes the local device store.
|
||||
//
|
||||
// If the logout request fails, the disconnection and local data deletion will not happen either.
|
||||
// If an error is returned, but you want to force disconnect/clear data, call Client.Disconnect() and Client.Store.Delete() manually.
|
||||
func (cli *Client) Logout() error {
|
||||
if cli.Store.ID == nil {
|
||||
return ErrNotLoggedIn
|
||||
}
|
||||
_, err := cli.sendIQ(infoQuery{
|
||||
Namespace: "md",
|
||||
Type: "set",
|
||||
To: types.ServerJID,
|
||||
Content: []waBinary.Node{{
|
||||
Tag: "remove-companion-device",
|
||||
Attrs: waBinary.Attrs{
|
||||
"jid": *cli.Store.ID,
|
||||
"reason": "user_initiated",
|
||||
},
|
||||
}},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error sending logout request: %w", err)
|
||||
}
|
||||
cli.Disconnect()
|
||||
err = cli.Store.Delete()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error deleting data from store: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddEventHandler registers a new function to receive all events emitted by this client.
|
||||
//
|
||||
// The returned integer is the event handler ID, which can be passed to RemoveEventHandler to remove it.
|
||||
//
|
||||
// All registered event handlers will receive all events. You should use a type switch statement to
|
||||
// filter the events you want:
|
||||
// func myEventHandler(evt interface{}) {
|
||||
// switch v := evt.(type) {
|
||||
// case *events.Message:
|
||||
// fmt.Println("Received a message!")
|
||||
// case *events.Receipt:
|
||||
// fmt.Println("Received a receipt!")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// If you want to access the Client instance inside the event handler, the recommended way is to
|
||||
// wrap the whole handler in another struct:
|
||||
// type MyClient struct {
|
||||
// WAClient *whatsmeow.Client
|
||||
// eventHandlerID uint32
|
||||
// }
|
||||
//
|
||||
// func (mycli *MyClient) register() {
|
||||
// mycli.eventHandlerID = mycli.WAClient.AddEventHandler(mycli.myEventHandler)
|
||||
// }
|
||||
//
|
||||
// func (mycli *MyClient) myEventHandler(evt interface{}) {
|
||||
// // Handle event and access mycli.WAClient
|
||||
// }
|
||||
func (cli *Client) AddEventHandler(handler EventHandler) uint32 {
|
||||
nextID := atomic.AddUint32(&nextHandlerID, 1)
|
||||
cli.eventHandlersLock.Lock()
|
||||
cli.eventHandlers = append(cli.eventHandlers, wrappedEventHandler{handler, nextID})
|
||||
cli.eventHandlersLock.Unlock()
|
||||
return nextID
|
||||
}
|
||||
|
||||
// RemoveEventHandler removes a previously registered event handler function.
|
||||
// If the function with the given ID is found, this returns true.
|
||||
//
|
||||
// N.B. Do not run this directly from an event handler. That would cause a deadlock because the
|
||||
// event dispatcher holds a read lock on the event handler list, and this method wants a write lock
|
||||
// on the same list. Instead run it in a goroutine:
|
||||
// func (mycli *MyClient) myEventHandler(evt interface{}) {
|
||||
// if noLongerWantEvents {
|
||||
// go mycli.WAClient.RemoveEventHandler(mycli.eventHandlerID)
|
||||
// }
|
||||
// }
|
||||
func (cli *Client) RemoveEventHandler(id uint32) bool {
|
||||
cli.eventHandlersLock.Lock()
|
||||
defer cli.eventHandlersLock.Unlock()
|
||||
for index := range cli.eventHandlers {
|
||||
if cli.eventHandlers[index].id == id {
|
||||
if index == 0 {
|
||||
cli.eventHandlers[0].fn = nil
|
||||
cli.eventHandlers = cli.eventHandlers[1:]
|
||||
return true
|
||||
} else if index < len(cli.eventHandlers)-1 {
|
||||
copy(cli.eventHandlers[index:], cli.eventHandlers[index+1:])
|
||||
}
|
||||
cli.eventHandlers[len(cli.eventHandlers)-1].fn = nil
|
||||
cli.eventHandlers = cli.eventHandlers[:len(cli.eventHandlers)-1]
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// RemoveEventHandlers removes all event handlers that have been registered with AddEventHandler
|
||||
func (cli *Client) RemoveEventHandlers() {
|
||||
cli.eventHandlersLock.Lock()
|
||||
cli.eventHandlers = make([]wrappedEventHandler, 0, 1)
|
||||
cli.eventHandlersLock.Unlock()
|
||||
}
|
||||
|
||||
func (cli *Client) handleFrame(data []byte) {
|
||||
decompressed, err := waBinary.Unpack(data)
|
||||
if err != nil {
|
||||
cli.Log.Warnf("Failed to decompress frame: %v", err)
|
||||
cli.Log.Debugf("Errored frame hex: %s", hex.EncodeToString(data))
|
||||
return
|
||||
}
|
||||
node, err := waBinary.Unmarshal(decompressed)
|
||||
if err != nil {
|
||||
cli.Log.Warnf("Failed to decode node in frame: %v", err)
|
||||
cli.Log.Debugf("Errored frame hex: %s", hex.EncodeToString(decompressed))
|
||||
return
|
||||
}
|
||||
cli.recvLog.Debugf("%s", node.XMLString())
|
||||
if node.Tag == "xmlstreamend" {
|
||||
if !cli.isExpectedDisconnect() {
|
||||
cli.Log.Warnf("Received stream end frame")
|
||||
}
|
||||
// TODO should we do something else?
|
||||
} else if cli.receiveResponse(node) {
|
||||
// handled
|
||||
} else if _, ok := cli.nodeHandlers[node.Tag]; ok {
|
||||
select {
|
||||
case cli.handlerQueue <- node:
|
||||
default:
|
||||
cli.Log.Warnf("Handler queue is full, message ordering is no longer guaranteed")
|
||||
go func() {
|
||||
cli.handlerQueue <- node
|
||||
}()
|
||||
}
|
||||
} else {
|
||||
cli.Log.Debugf("Didn't handle WhatsApp node %s", node.Tag)
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *Client) handlerQueueLoop(ctx context.Context) {
|
||||
for {
|
||||
select {
|
||||
case node := <-cli.handlerQueue:
|
||||
cli.nodeHandlers[node.Tag](node)
|
||||
case <-ctx.Done():
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
func (cli *Client) sendNode(node waBinary.Node) error {
|
||||
cli.socketLock.RLock()
|
||||
sock := cli.socket
|
||||
cli.socketLock.RUnlock()
|
||||
if sock == nil {
|
||||
return ErrNotConnected
|
||||
}
|
||||
|
||||
payload, err := waBinary.Marshal(node)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to marshal node: %w", err)
|
||||
}
|
||||
|
||||
cli.sendLog.Debugf("%s", node.XMLString())
|
||||
return sock.SendFrame(payload)
|
||||
}
|
||||
|
||||
func (cli *Client) dispatchEvent(evt interface{}) {
|
||||
cli.eventHandlersLock.RLock()
|
||||
defer func() {
|
||||
cli.eventHandlersLock.RUnlock()
|
||||
err := recover()
|
||||
if err != nil {
|
||||
cli.Log.Errorf("Event handler panicked while handling a %T: %v\n%s", evt, err, debug.Stack())
|
||||
}
|
||||
}()
|
||||
for _, handler := range cli.eventHandlers {
|
||||
handler.fn(evt)
|
||||
}
|
||||
}
|
||||
141
vendor/go.mau.fi/whatsmeow/connectionevents.go
vendored
Normal file
141
vendor/go.mau.fi/whatsmeow/connectionevents.go
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
// Copyright (c) 2021 Tulir Asokan
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public
|
||||
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
package whatsmeow
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
waBinary "go.mau.fi/whatsmeow/binary"
|
||||
"go.mau.fi/whatsmeow/types"
|
||||
"go.mau.fi/whatsmeow/types/events"
|
||||
)
|
||||
|
||||
func (cli *Client) handleStreamError(node *waBinary.Node) {
|
||||
atomic.StoreUint32(&cli.isLoggedIn, 0)
|
||||
code, _ := node.Attrs["code"].(string)
|
||||
conflict, _ := node.GetOptionalChildByTag("conflict")
|
||||
conflictType := conflict.AttrGetter().OptionalString("type")
|
||||
switch {
|
||||
case code == "515":
|
||||
cli.Log.Infof("Got 515 code, reconnecting...")
|
||||
go func() {
|
||||
cli.Disconnect()
|
||||
err := cli.Connect()
|
||||
if err != nil {
|
||||
cli.Log.Errorf("Failed to reconnect after 515 code:", err)
|
||||
}
|
||||
}()
|
||||
case code == "401" && conflictType == "device_removed":
|
||||
cli.expectDisconnect()
|
||||
cli.Log.Infof("Got device removed stream error, sending LoggedOut event and deleting session")
|
||||
go cli.dispatchEvent(&events.LoggedOut{OnConnect: false})
|
||||
err := cli.Store.Delete()
|
||||
if err != nil {
|
||||
cli.Log.Warnf("Failed to delete store after device_removed error: %v", err)
|
||||
}
|
||||
case conflictType == "replaced":
|
||||
cli.expectDisconnect()
|
||||
cli.Log.Infof("Got replaced stream error, sending StreamReplaced event")
|
||||
go cli.dispatchEvent(&events.StreamReplaced{})
|
||||
case code == "503":
|
||||
// This seems to happen when the server wants to restart or something.
|
||||
// The disconnection will be emitted as an events.Disconnected and then the auto-reconnect will do its thing.
|
||||
cli.Log.Warnf("Got 503 stream error, assuming automatic reconnect will handle it")
|
||||
default:
|
||||
cli.Log.Errorf("Unknown stream error: %s", node.XMLString())
|
||||
go cli.dispatchEvent(&events.StreamError{Code: code, Raw: node})
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *Client) handleIB(node *waBinary.Node) {
|
||||
children := node.GetChildren()
|
||||
for _, child := range children {
|
||||
ag := child.AttrGetter()
|
||||
switch child.Tag {
|
||||
case "downgrade_webclient":
|
||||
go cli.dispatchEvent(&events.QRScannedWithoutMultidevice{})
|
||||
case "offline_preview":
|
||||
cli.dispatchEvent(&events.OfflineSyncPreview{
|
||||
Total: ag.Int("count"),
|
||||
AppDataChanges: ag.Int("appdata"),
|
||||
Messages: ag.Int("message"),
|
||||
Notifications: ag.Int("notification"),
|
||||
Receipts: ag.Int("receipt"),
|
||||
})
|
||||
case "offline":
|
||||
cli.dispatchEvent(&events.OfflineSyncCompleted{
|
||||
Count: ag.Int("count"),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *Client) handleConnectFailure(node *waBinary.Node) {
|
||||
ag := node.AttrGetter()
|
||||
reason := ag.String("reason")
|
||||
if reason == "401" {
|
||||
cli.expectDisconnect()
|
||||
cli.Log.Infof("Got 401 connect failure, sending LoggedOut event and deleting session")
|
||||
go cli.dispatchEvent(&events.LoggedOut{OnConnect: true})
|
||||
err := cli.Store.Delete()
|
||||
if err != nil {
|
||||
cli.Log.Warnf("Failed to delete store after 401 failure: %v", err)
|
||||
}
|
||||
} else {
|
||||
cli.expectDisconnect()
|
||||
cli.Log.Warnf("Unknown connect failure: %s", node.XMLString())
|
||||
go cli.dispatchEvent(&events.ConnectFailure{Reason: reason, Raw: node})
|
||||
}
|
||||
}
|
||||
|
||||
func (cli *Client) handleConnectSuccess(node *waBinary.Node) {
|
||||
cli.Log.Infof("Successfully authenticated")
|
||||
cli.LastSuccessfulConnect = time.Now()
|
||||
cli.AutoReconnectErrors = 0
|
||||
atomic.StoreUint32(&cli.isLoggedIn, 1)
|
||||
go func() {
|
||||
if dbCount, err := cli.Store.PreKeys.UploadedPreKeyCount(); err != nil {
|
||||
cli.Log.Errorf("Failed to get number of prekeys in database: %v", err)
|
||||
} else if serverCount, err := cli.getServerPreKeyCount(); err != nil {
|
||||
cli.Log.Warnf("Failed to get number of prekeys on server: %v", err)
|
||||
} else {
|
||||
cli.Log.Debugf("Database has %d prekeys, server says we have %d", dbCount, serverCount)
|
||||
if serverCount < MinPreKeyCount || dbCount < MinPreKeyCount {
|
||||
cli.uploadPreKeys()
|
||||
sc, _ := cli.getServerPreKeyCount()
|
||||
cli.Log.Debugf("Prekey count after upload: %d", sc)
|
||||
}
|
||||
}
|
||||
err := cli.SetPassive(false)
|
||||
if err != nil {
|
||||
cli.Log.Warnf("Failed to send post-connect passive IQ: %v", err)
|
||||
}
|
||||
cli.dispatchEvent(&events.Connected{})
|
||||
}()
|
||||
}
|
||||
|
||||
// SetPassive tells the WhatsApp server whether this device is passive or not.
|
||||
//
|
||||
// This seems to mostly affect whether the device receives certain events.
|
||||
// By default, whatsmeow will automatically do SetPassive(false) after connecting.
|
||||
func (cli *Client) SetPassive(passive bool) error {
|
||||
tag := "active"
|
||||
if passive {
|
||||
tag = "passive"
|
||||
}
|
||||
_, err := cli.sendIQ(infoQuery{
|
||||
Namespace: "passive",
|
||||
Type: "set",
|
||||
To: types.ServerJID,
|
||||
Content: []waBinary.Node{{Tag: tag}},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user