4
0
mirror of https://github.com/cwinfo/matterbridge.git synced 2025-06-27 06:29:24 +00:00

Add dependencies/vendor (whatsapp)

This commit is contained in:
Wim
2022-01-31 00:27:37 +01:00
parent e7b193788a
commit e3cafeaf92
1074 changed files with 3091569 additions and 26075 deletions

674
vendor/go.mau.fi/libsignal/LICENSE vendored Normal file
View 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
View 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
}

View 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
View 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
}

View 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
}

View 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
View File

@ -0,0 +1,3 @@
// Package ecc provides a way to generate, sign, and use Elliptic-Curve
// X25519 Cryptography keys.
package ecc

View 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
}

View 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
}

View 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
}

View 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[:])
}

View 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()
}

View 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
}

View File

@ -0,0 +1,3 @@
// Package ratchet provides the methods necessary to establish a ratchet
// session for group messaging.
package ratchet

View 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)
}

View 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
}

View File

@ -0,0 +1,2 @@
// Package record provides the state and record of a group session.
package record

View 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,
}
}

View 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
}

View File

@ -0,0 +1,3 @@
// Package store provides the storage interfaces for storing group sender
// key records.
package store

View 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
View 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
}

View 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
}

View 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()
}

View 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 {
//}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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, ",")
}

View 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)
}

View 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

View File

@ -0,0 +1,3 @@
// Package protocol provides address, group, and message structures that
// the Signal protocol uses for sending encrypted messages.
package protocol

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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)
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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
}

View 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;
}

View 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
}

File diff suppressed because it is too large Load Diff

View 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;
}

View 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
}

View 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
}

View 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
}

View 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;
}

View 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
}

View 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
}

View 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")

View 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,
}
}

View File

@ -0,0 +1,3 @@
// Package record provides the state and record of an ongoing double
// ratchet session.
package record

View 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()),
}
}

View 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
}

View 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)
}

View 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,
}
}

View 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,
}
}

View 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)
}

View 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(),
}
}

View File

@ -0,0 +1,3 @@
// Package store provides the storage interfaces for storing the state of
// ongoing double ratchet sessions and keys.
package store

View 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
}

View 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)
}

View 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)
}

View 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()
}

View 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
}

View 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)
}

View 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
}

View 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()
}

View 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 --------------

View File

@ -0,0 +1,4 @@
package medium
// MaxValue is the maximum possible integer value.
const MaxValue uint32 = 0xFFFFFF

View 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
}