From 09938ab16cf16eb8e7d8ddc752eb89b34e23ca0d Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Fri, 23 Mar 2018 10:12:28 -0700 Subject: [PATCH] Big bang --- .gitignore | 7 + LICENSE | 674 +++ android/build.gradle | 104 + android/src/main/AndroidManifest.xml | 3 + build.gradle | 13 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 51010 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 164 + gradlew.bat | 90 + java/build.gradle | 109 + .../InvalidMetadataMessageException.java | 13 + .../InvalidMetadataVersionException.java | 8 + .../ProtocolDuplicateMessageException.java | 8 + .../libsignal/metadata/ProtocolException.java | 22 + .../metadata/ProtocolInvalidKeyException.java | 10 + .../ProtocolInvalidKeyIdException.java | 8 + .../ProtocolInvalidMessageException.java | 10 + .../ProtocolInvalidVersionException.java | 10 + .../ProtocolLegacyMessageException.java | 10 + .../metadata/ProtocolNoSessionException.java | 10 + .../ProtocolUntrustedIdentityException.java | 10 + .../metadata/SealedSessionCipher.java | 254 ++ .../libsignal/metadata/SelfSendException.java | 5 + .../libsignal/metadata/SignalProtos.java | 3804 +++++++++++++++++ .../certificate/CertificateValidator.java | 56 + .../InvalidCertificateException.java | 12 + .../certificate/SenderCertificate.java | 84 + .../certificate/ServerCertificate.java | 64 + .../protocol/UnidentifiedSenderMessage.java | 88 + .../UnidentifiedSenderMessageContent.java | 84 + protobuf/Makefile | 3 + protobuf/UnidentifiedDelivery.proto | 45 + settings.gradle | 1 + tests/build.gradle | 12 + .../metadata/SealedSessionCipherTest.java | 175 + .../libsignal/metadata/SessionCipherTest.java | 193 + .../TestInMemorySignalProtocolStore.java | 27 + .../certificate/SenderCertificateTest.java | 127 + .../certificate/ServerCertificateTest.java | 117 + 39 files changed, 6440 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 android/build.gradle create mode 100644 android/src/main/AndroidManifest.xml create mode 100644 build.gradle create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 java/build.gradle create mode 100644 java/src/main/java/org/signal/libsignal/metadata/InvalidMetadataMessageException.java create mode 100644 java/src/main/java/org/signal/libsignal/metadata/InvalidMetadataVersionException.java create mode 100644 java/src/main/java/org/signal/libsignal/metadata/ProtocolDuplicateMessageException.java create mode 100644 java/src/main/java/org/signal/libsignal/metadata/ProtocolException.java create mode 100644 java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidKeyException.java create mode 100644 java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidKeyIdException.java create mode 100644 java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidMessageException.java create mode 100644 java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidVersionException.java create mode 100644 java/src/main/java/org/signal/libsignal/metadata/ProtocolLegacyMessageException.java create mode 100644 java/src/main/java/org/signal/libsignal/metadata/ProtocolNoSessionException.java create mode 100644 java/src/main/java/org/signal/libsignal/metadata/ProtocolUntrustedIdentityException.java create mode 100644 java/src/main/java/org/signal/libsignal/metadata/SealedSessionCipher.java create mode 100644 java/src/main/java/org/signal/libsignal/metadata/SelfSendException.java create mode 100644 java/src/main/java/org/signal/libsignal/metadata/SignalProtos.java create mode 100644 java/src/main/java/org/signal/libsignal/metadata/certificate/CertificateValidator.java create mode 100644 java/src/main/java/org/signal/libsignal/metadata/certificate/InvalidCertificateException.java create mode 100644 java/src/main/java/org/signal/libsignal/metadata/certificate/SenderCertificate.java create mode 100644 java/src/main/java/org/signal/libsignal/metadata/certificate/ServerCertificate.java create mode 100644 java/src/main/java/org/signal/libsignal/metadata/protocol/UnidentifiedSenderMessage.java create mode 100644 java/src/main/java/org/signal/libsignal/metadata/protocol/UnidentifiedSenderMessageContent.java create mode 100644 protobuf/Makefile create mode 100644 protobuf/UnidentifiedDelivery.proto create mode 100644 settings.gradle create mode 100644 tests/build.gradle create mode 100644 tests/src/test/java/org/signal/libsignal/metadata/SealedSessionCipherTest.java create mode 100644 tests/src/test/java/org/signal/libsignal/metadata/SessionCipherTest.java create mode 100644 tests/src/test/java/org/signal/libsignal/metadata/TestInMemorySignalProtocolStore.java create mode 100644 tests/src/test/java/org/signal/libsignal/metadata/certificate/SenderCertificateTest.java create mode 100644 tests/src/test/java/org/signal/libsignal/metadata/certificate/ServerCertificateTest.java diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b7e3f28 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +build +/obj +*.iml +.gradle +.idea +gradle.properties +local.properties diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + 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. + + + Copyright (C) + + 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 . + +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: + + Copyright (C) + 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 +. + + 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 +. diff --git a/android/build.gradle b/android/build.gradle new file mode 100644 index 0000000..6408507 --- /dev/null +++ b/android/build.gradle @@ -0,0 +1,104 @@ +buildscript { + repositories { + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:2.2.0' + } +} + +apply plugin: 'com.android.library' +apply plugin: 'maven' +apply plugin: 'signing' + +archivesBaseName = "signal-metadata-android" +version = version_number +group = group_info + +android { + compileSdkVersion 21 + buildToolsVersion "21.1.2" + + sourceSets { + androidTest { + java.srcDirs = ['src/androidTest/java', project(':tests').file('src/test/java')] + } + } + + libraryVariants.all { variant -> + variant.outputs.each { output -> + def outputFile = output.outputFile + if (outputFile != null && outputFile.name.endsWith('.aar')) { + def fileName = "${archivesBaseName}-${version}.aar" + output.outputFile = new File(outputFile.parent, fileName) + } + } + } +} + +repositories { + mavenCentral() + mavenLocal() +} + +dependencies { + compile "org.whispersystems:signal-protocol-android:${protocol_version}" + compile (project(':java')) { + exclude group: 'org.whispersystems', module: 'signal-protocol-java' + } +} + +signing { + required { has("release") && gradle.taskGraph.hasTask("uploadArchives") } + sign configurations.archives +} + +uploadArchives { + configuration = configurations.archives + repositories.mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: sonatypeRepo) { + authentication(userName: whisperSonatypeUsername, password: whisperSonatypePassword) + } + + pom.project { + name 'signal-metadata-android' + packaging 'aar' + description 'Signal Protocol Plus cryptography library for Android' + url 'https://github.com/SignalApp/libsignal-metadata-java' + + scm { + url 'scm:git@github.com:SignalApp/libsignal-metadata-java.git' + connection 'scm:git@github.com:SignalApp/libsignal-metadata-java.git' + developerConnection 'scm:git@github.com:SignalApp/libsignal-metadata-java.git' + } + + licenses { + license { + name 'GPLv3' + url 'https://www.gnu.org/licenses/gpl-3.0.txt' + distribution 'repo' + } + } + + developers { + developer { + name 'Moxie Marlinspike' + } + } + } + } +} + +task installArchives(type: Upload) { + description "Installs the artifacts to the local Maven repository." + configuration = configurations['archives'] + repositories { + mavenDeployer { + repository url: "file://${System.properties['user.home']}/.m2/repository" + } + } +} + diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..794e689 --- /dev/null +++ b/android/src/main/AndroidManifest.xml @@ -0,0 +1,3 @@ + + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..7606998 --- /dev/null +++ b/build.gradle @@ -0,0 +1,13 @@ +subprojects { + ext.version_number = "0.0.2" + ext.group_info = "org.signal" + ext.protocol_version = "2.7.0" + + if (JavaVersion.current().isJava8Compatible()) { + allprojects { + tasks.withType(Javadoc) { + options.addStringOption('Xdoclint:none', '-quiet') + } + } + } +} diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..2322723c7ed5f591adfa4c87b6c51932f591249a GIT binary patch literal 51010 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∓LFg$ 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 literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..b5ffdfd --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Mon Oct 17 15:38:50 PDT 2016 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..91a7e26 --- /dev/null +++ b/gradlew @@ -0,0 +1,164 @@ +#!/usr/bin/env bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >&- +APP_HOME="`pwd -P`" +cd "$SAVED" >&- + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..aec9973 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/java/build.gradle b/java/build.gradle new file mode 100644 index 0000000..adee3bd --- /dev/null +++ b/java/build.gradle @@ -0,0 +1,109 @@ +apply plugin: 'java' +apply plugin: 'maven' +apply plugin: 'signing' + +sourceCompatibility = 1.7 +archivesBaseName = "signal-metadata-java" +version = version_number +group = group_info + +repositories { + mavenCentral() + mavenLocal() +} + +sourceSets { + test { + java { + srcDirs = ['src/test/java/', project(':tests').file('src/test/java')] + } + } +} + +dependencies { + compile "org.whispersystems:signal-protocol-java:${protocol_version}" + + testCompile ('junit:junit:3.8.2') +} + + +test { + testLogging { + events 'passed' + showStandardStreams = true + } + + include 'org/signal/**' +} + +signing { + required { has("release") && gradle.taskGraph.hasTask("uploadArchives") } + sign configurations.archives +} + +uploadArchives { + configuration = configurations.archives + repositories.mavenDeployer { + beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } + + repository(url: sonatypeRepo) { + authentication(userName: whisperSonatypeUsername, password: whisperSonatypePassword) + } + + pom.project { + name 'signal-metadata-java' + packaging 'jar' + description 'Signal Protocol Plus cryptography library for Java' + url 'https://github.com/signalapp/libsignal-metadata-java' + + scm { + url 'scm:git@github.com:SignalApp/libsignal-metadata-java.git' + connection 'scm:git@github.com:SignalApp/libsignal-metadata-java.git' + developerConnection 'scm:git@github.com:SignalApp/libsignal-metadata-java.git' + } + + licenses { + license { + name 'GPLv3' + url 'https://www.gnu.org/licenses/gpl-3.0.txt' + distribution 'repo' + } + } + + developers { + developer { + name 'Moxie Marlinspike' + } + } + } + } +} + +task installArchives(type: Upload) { + description "Installs the artifacts to the local Maven repository." + configuration = configurations['archives'] + repositories { + mavenDeployer { + repository url: "file://${System.properties['user.home']}/.m2/repository" + } + } +} + +task packageJavadoc(type: Jar, dependsOn: 'javadoc') { + from javadoc.destinationDir + classifier = 'javadoc' +} + +task packageSources(type: Jar) { + from sourceSets.main.allSource + classifier = 'sources' +} + +artifacts { + archives(packageJavadoc) { + type = 'javadoc' + } + + archives packageSources +} + diff --git a/java/src/main/java/org/signal/libsignal/metadata/InvalidMetadataMessageException.java b/java/src/main/java/org/signal/libsignal/metadata/InvalidMetadataMessageException.java new file mode 100644 index 0000000..01aa341 --- /dev/null +++ b/java/src/main/java/org/signal/libsignal/metadata/InvalidMetadataMessageException.java @@ -0,0 +1,13 @@ +package org.signal.libsignal.metadata; + + +public class InvalidMetadataMessageException extends Exception { + public InvalidMetadataMessageException(String s) { + super(s); + } + + public InvalidMetadataMessageException(Exception s) { + super(s); + } + +} diff --git a/java/src/main/java/org/signal/libsignal/metadata/InvalidMetadataVersionException.java b/java/src/main/java/org/signal/libsignal/metadata/InvalidMetadataVersionException.java new file mode 100644 index 0000000..da52bd8 --- /dev/null +++ b/java/src/main/java/org/signal/libsignal/metadata/InvalidMetadataVersionException.java @@ -0,0 +1,8 @@ +package org.signal.libsignal.metadata; + + +public class InvalidMetadataVersionException extends Exception { + public InvalidMetadataVersionException(String s) { + super(s); + } +} diff --git a/java/src/main/java/org/signal/libsignal/metadata/ProtocolDuplicateMessageException.java b/java/src/main/java/org/signal/libsignal/metadata/ProtocolDuplicateMessageException.java new file mode 100644 index 0000000..1da79b2 --- /dev/null +++ b/java/src/main/java/org/signal/libsignal/metadata/ProtocolDuplicateMessageException.java @@ -0,0 +1,8 @@ +package org.signal.libsignal.metadata; + + +public class ProtocolDuplicateMessageException extends ProtocolException { + public ProtocolDuplicateMessageException(Exception e, String sender, int senderDevice) { + super(e, sender, senderDevice); + } +} diff --git a/java/src/main/java/org/signal/libsignal/metadata/ProtocolException.java b/java/src/main/java/org/signal/libsignal/metadata/ProtocolException.java new file mode 100644 index 0000000..47bf4f7 --- /dev/null +++ b/java/src/main/java/org/signal/libsignal/metadata/ProtocolException.java @@ -0,0 +1,22 @@ +package org.signal.libsignal.metadata; + + +public abstract class ProtocolException extends Exception { + + private final String sender; + private final int senderDevice; + + public ProtocolException(Exception e, String sender, int senderDevice) { + super(e); + this.sender = sender; + this.senderDevice = senderDevice; + } + + public String getSender() { + return sender; + } + + public int getSenderDevice() { + return senderDevice; + } +} diff --git a/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidKeyException.java b/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidKeyException.java new file mode 100644 index 0000000..013ce6c --- /dev/null +++ b/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidKeyException.java @@ -0,0 +1,10 @@ +package org.signal.libsignal.metadata; + + +import org.whispersystems.libsignal.InvalidKeyException; + +public class ProtocolInvalidKeyException extends ProtocolException { + public ProtocolInvalidKeyException(InvalidKeyException e, String sender, int senderDevice) { + super(e, sender, senderDevice); + } +} diff --git a/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidKeyIdException.java b/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidKeyIdException.java new file mode 100644 index 0000000..de9526a --- /dev/null +++ b/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidKeyIdException.java @@ -0,0 +1,8 @@ +package org.signal.libsignal.metadata; + + +public class ProtocolInvalidKeyIdException extends ProtocolException { + public ProtocolInvalidKeyIdException(Exception e, String sender, int senderDevice) { + super(e, sender, senderDevice); + } +} diff --git a/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidMessageException.java b/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidMessageException.java new file mode 100644 index 0000000..d50d39f --- /dev/null +++ b/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidMessageException.java @@ -0,0 +1,10 @@ +package org.signal.libsignal.metadata; + + +import org.whispersystems.libsignal.InvalidMessageException; + +public class ProtocolInvalidMessageException extends ProtocolException { + public ProtocolInvalidMessageException(InvalidMessageException e, String sender, int senderDevice) { + super(e, sender, senderDevice); + } +} diff --git a/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidVersionException.java b/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidVersionException.java new file mode 100644 index 0000000..571534c --- /dev/null +++ b/java/src/main/java/org/signal/libsignal/metadata/ProtocolInvalidVersionException.java @@ -0,0 +1,10 @@ +package org.signal.libsignal.metadata; + + +import org.whispersystems.libsignal.InvalidVersionException; + +public class ProtocolInvalidVersionException extends ProtocolException { + public ProtocolInvalidVersionException(InvalidVersionException e, String sender, int senderDevice) { + super(e, sender, senderDevice); + } +} diff --git a/java/src/main/java/org/signal/libsignal/metadata/ProtocolLegacyMessageException.java b/java/src/main/java/org/signal/libsignal/metadata/ProtocolLegacyMessageException.java new file mode 100644 index 0000000..e2f8490 --- /dev/null +++ b/java/src/main/java/org/signal/libsignal/metadata/ProtocolLegacyMessageException.java @@ -0,0 +1,10 @@ +package org.signal.libsignal.metadata; + + +import org.whispersystems.libsignal.LegacyMessageException; + +public class ProtocolLegacyMessageException extends ProtocolException { + public ProtocolLegacyMessageException(LegacyMessageException e, String sender, int senderDeviceId) { + super(e, sender, senderDeviceId); + } +} diff --git a/java/src/main/java/org/signal/libsignal/metadata/ProtocolNoSessionException.java b/java/src/main/java/org/signal/libsignal/metadata/ProtocolNoSessionException.java new file mode 100644 index 0000000..5b1d0c7 --- /dev/null +++ b/java/src/main/java/org/signal/libsignal/metadata/ProtocolNoSessionException.java @@ -0,0 +1,10 @@ +package org.signal.libsignal.metadata; + + +import org.whispersystems.libsignal.NoSessionException; + +public class ProtocolNoSessionException extends ProtocolException { + public ProtocolNoSessionException(NoSessionException e, String sender, int senderDevice) { + super(e, sender, senderDevice); + } +} diff --git a/java/src/main/java/org/signal/libsignal/metadata/ProtocolUntrustedIdentityException.java b/java/src/main/java/org/signal/libsignal/metadata/ProtocolUntrustedIdentityException.java new file mode 100644 index 0000000..2253f50 --- /dev/null +++ b/java/src/main/java/org/signal/libsignal/metadata/ProtocolUntrustedIdentityException.java @@ -0,0 +1,10 @@ +package org.signal.libsignal.metadata; + + +import org.whispersystems.libsignal.UntrustedIdentityException; + +public class ProtocolUntrustedIdentityException extends ProtocolException { + public ProtocolUntrustedIdentityException(UntrustedIdentityException e, String sender, int senderDevice) { + super(e, sender, senderDevice); + } +} diff --git a/java/src/main/java/org/signal/libsignal/metadata/SealedSessionCipher.java b/java/src/main/java/org/signal/libsignal/metadata/SealedSessionCipher.java new file mode 100644 index 0000000..8f5a77a --- /dev/null +++ b/java/src/main/java/org/signal/libsignal/metadata/SealedSessionCipher.java @@ -0,0 +1,254 @@ +package org.signal.libsignal.metadata; + + +import org.signal.libsignal.metadata.certificate.CertificateValidator; +import org.signal.libsignal.metadata.certificate.InvalidCertificateException; +import org.signal.libsignal.metadata.certificate.SenderCertificate; +import org.signal.libsignal.metadata.protocol.UnidentifiedSenderMessage; +import org.signal.libsignal.metadata.protocol.UnidentifiedSenderMessageContent; +import org.whispersystems.libsignal.DuplicateMessageException; +import org.whispersystems.libsignal.IdentityKeyPair; +import org.whispersystems.libsignal.InvalidKeyException; +import org.whispersystems.libsignal.InvalidKeyIdException; +import org.whispersystems.libsignal.InvalidMacException; +import org.whispersystems.libsignal.InvalidMessageException; +import org.whispersystems.libsignal.InvalidVersionException; +import org.whispersystems.libsignal.LegacyMessageException; +import org.whispersystems.libsignal.NoSessionException; +import org.whispersystems.libsignal.SessionCipher; +import org.whispersystems.libsignal.SignalProtocolAddress; +import org.whispersystems.libsignal.UntrustedIdentityException; +import org.whispersystems.libsignal.ecc.Curve; +import org.whispersystems.libsignal.ecc.ECKeyPair; +import org.whispersystems.libsignal.ecc.ECPrivateKey; +import org.whispersystems.libsignal.ecc.ECPublicKey; +import org.whispersystems.libsignal.kdf.HKDFv3; +import org.whispersystems.libsignal.protocol.CiphertextMessage; +import org.whispersystems.libsignal.protocol.PreKeySignalMessage; +import org.whispersystems.libsignal.protocol.SignalMessage; +import org.whispersystems.libsignal.state.SignalProtocolStore; +import org.whispersystems.libsignal.util.ByteUtil; +import org.whispersystems.libsignal.util.Pair; + +import java.security.InvalidAlgorithmParameterException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.text.ParseException; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.Mac; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class SealedSessionCipher { + + private final SignalProtocolStore signalProtocolStore; + private final SignalProtocolAddress localAddress; + + public SealedSessionCipher(SignalProtocolStore signalProtocolStore, + SignalProtocolAddress localAddress) + { + this.signalProtocolStore = signalProtocolStore; + this.localAddress = localAddress; + } + + public byte[] encrypt(SignalProtocolAddress destinationAddress, SenderCertificate senderCertificate, byte[] paddedPlaintext) + throws InvalidKeyException, UntrustedIdentityException + { + CiphertextMessage message = new SessionCipher(signalProtocolStore, destinationAddress).encrypt(paddedPlaintext); + IdentityKeyPair ourIdentity = signalProtocolStore.getIdentityKeyPair(); + ECPublicKey theirIdentity = signalProtocolStore.getIdentity(destinationAddress).getPublicKey(); + + ECKeyPair ephemeral = Curve.generateKeyPair(); + byte[] ephemeralSalt = ByteUtil.combine("UnidentifiedDelivery".getBytes(), theirIdentity.serialize(), ephemeral.getPublicKey().serialize()); + EphemeralKeys ephemeralKeys = calculateEphemeralKeys(theirIdentity, ephemeral.getPrivateKey(), ephemeralSalt); + byte[] staticKeyCiphertext = encrypt(ephemeralKeys.cipherKey, ephemeralKeys.macKey, ourIdentity.getPublicKey().getPublicKey().serialize()); + + byte[] staticSalt = ByteUtil.combine(ephemeralKeys.chainKey, staticKeyCiphertext); + StaticKeys staticKeys = calculateStaticKeys(theirIdentity, ourIdentity.getPrivateKey(), staticSalt); + UnidentifiedSenderMessageContent content = new UnidentifiedSenderMessageContent(message.getType(), senderCertificate, message.serialize()); + byte[] messageBytes = encrypt(staticKeys.cipherKey, staticKeys.macKey, content.getSerialized()); + + return new UnidentifiedSenderMessage(ephemeral.getPublicKey(), staticKeyCiphertext, messageBytes).getSerialized(); + } + + public Pair decrypt(CertificateValidator validator, byte[] ciphertext, long timestamp) + throws + InvalidMetadataMessageException, InvalidMetadataVersionException, + ProtocolInvalidMessageException, ProtocolInvalidKeyException, + ProtocolNoSessionException, ProtocolLegacyMessageException, + ProtocolInvalidVersionException, ProtocolDuplicateMessageException, + ProtocolInvalidKeyIdException, ProtocolUntrustedIdentityException, + SelfSendException + { + UnidentifiedSenderMessageContent content; + + try { + IdentityKeyPair ourIdentity = signalProtocolStore.getIdentityKeyPair(); + UnidentifiedSenderMessage wrapper = new UnidentifiedSenderMessage(ciphertext); + byte[] ephemeralSalt = ByteUtil.combine("UnidentifiedDelivery".getBytes(), ourIdentity.getPublicKey().getPublicKey().serialize(), wrapper.getEphemeral().serialize()); + EphemeralKeys ephemeralKeys = calculateEphemeralKeys(wrapper.getEphemeral(), ourIdentity.getPrivateKey(), ephemeralSalt); + byte[] staticKeyBytes = decrypt(ephemeralKeys.cipherKey, ephemeralKeys.macKey, wrapper.getEncryptedStatic()); + + ECPublicKey staticKey = Curve.decodePoint(staticKeyBytes, 0); + byte[] staticSalt = ByteUtil.combine(ephemeralKeys.chainKey, wrapper.getEncryptedStatic()); + StaticKeys staticKeys = calculateStaticKeys(staticKey, ourIdentity.getPrivateKey(), staticSalt); + byte[] messageBytes = decrypt(staticKeys.cipherKey, staticKeys.macKey, wrapper.getEncryptedMessage()); + + content = new UnidentifiedSenderMessageContent(messageBytes); + validator.validate(content.getSenderCertificate(), timestamp); + + if (!MessageDigest.isEqual(content.getSenderCertificate().getKey().serialize(), staticKeyBytes)) { + throw new InvalidKeyException("Sender's certificate key does not match key used in message"); + } + + if (content.getSenderCertificate().getSender().equals(localAddress.getName()) && + content.getSenderCertificate().getSenderDeviceId() == localAddress.getDeviceId()) + { + throw new SelfSendException(); + } + } catch (InvalidKeyException | InvalidMacException | InvalidCertificateException e) { + throw new InvalidMetadataMessageException(e); + } + + try { + return new Pair<>(new SignalProtocolAddress(content.getSenderCertificate().getSender(), + content.getSenderCertificate().getSenderDeviceId()), + decrypt(content)); + } catch (InvalidMessageException e) { + throw new ProtocolInvalidMessageException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); + } catch (InvalidKeyException e) { + throw new ProtocolInvalidKeyException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); + } catch (NoSessionException e) { + throw new ProtocolNoSessionException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); + } catch (LegacyMessageException e) { + throw new ProtocolLegacyMessageException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); + } catch (InvalidVersionException e) { + throw new ProtocolInvalidVersionException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); + } catch (DuplicateMessageException e) { + throw new ProtocolDuplicateMessageException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); + } catch (InvalidKeyIdException e) { + throw new ProtocolInvalidKeyIdException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); + } catch (UntrustedIdentityException e) { + throw new ProtocolUntrustedIdentityException(e, content.getSenderCertificate().getSender(), content.getSenderCertificate().getSenderDeviceId()); + } + } + + public int getSessionVersion(SignalProtocolAddress remoteAddress) { + return new SessionCipher(signalProtocolStore, remoteAddress).getSessionVersion(); + } + + public int getRemoteRegistrationId(SignalProtocolAddress remoteAddress) { + return new SessionCipher(signalProtocolStore, remoteAddress).getRemoteRegistrationId(); + } + + private EphemeralKeys calculateEphemeralKeys(ECPublicKey ephemeralPublic, ECPrivateKey ephemeralPrivate, byte[] salt) throws InvalidKeyException { + try { + byte[] ephemeralSecret = Curve.calculateAgreement(ephemeralPublic, ephemeralPrivate); + byte[] ephemeralDerived = new HKDFv3().deriveSecrets(ephemeralSecret, salt, new byte[0], 96); + byte[][] ephemeralDerivedParts = ByteUtil.split(ephemeralDerived, 32, 32, 32); + + return new EphemeralKeys(ephemeralDerivedParts[0], ephemeralDerivedParts[1], ephemeralDerivedParts[2]); + } catch (ParseException e) { + throw new AssertionError(e); + } + } + + private StaticKeys calculateStaticKeys(ECPublicKey staticPublic, ECPrivateKey staticPrivate, byte[] salt) throws InvalidKeyException { + try { + byte[] staticSecret = Curve.calculateAgreement(staticPublic, staticPrivate); + byte[] staticDerived = new HKDFv3().deriveSecrets(staticSecret, salt, new byte[0], 96); + byte[][] staticDerivedParts = ByteUtil.split(staticDerived, 32, 32, 32); + + return new StaticKeys(staticDerivedParts[1], staticDerivedParts[2]); + } catch (ParseException e) { + throw new AssertionError(e); + } + } + + private byte[] decrypt(UnidentifiedSenderMessageContent message) + throws InvalidVersionException, InvalidMessageException, InvalidKeyException, DuplicateMessageException, InvalidKeyIdException, UntrustedIdentityException, LegacyMessageException, NoSessionException + { + + SignalProtocolAddress sender = new SignalProtocolAddress(message.getSenderCertificate().getSender(), message.getSenderCertificate().getSenderDeviceId()); + + switch (message.getType()) { + case CiphertextMessage.WHISPER_TYPE: return new SessionCipher(signalProtocolStore, sender).decrypt(new SignalMessage(message.getContent())); + case CiphertextMessage.PREKEY_TYPE: return new SessionCipher(signalProtocolStore, sender).decrypt(new PreKeySignalMessage(message.getContent())); + default: throw new InvalidMessageException("Unknown type: " + message.getType()); + } + } + + private byte[] encrypt(SecretKeySpec cipherKey, SecretKeySpec macKey, byte[] plaintext) { + try { + Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); + cipher.init(Cipher.ENCRYPT_MODE, cipherKey, new IvParameterSpec(new byte[16])); + + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(macKey); + + byte[] ciphertext = cipher.doFinal(plaintext); + byte[] ourFullMac = mac.doFinal(ciphertext); + byte[] ourMac = ByteUtil.trim(ourFullMac, 10); + + return ByteUtil.combine(ciphertext, ourMac); + } catch (NoSuchAlgorithmException | NoSuchPaddingException | java.security.InvalidKeyException | BadPaddingException | IllegalBlockSizeException | InvalidAlgorithmParameterException e) { + throw new AssertionError(e); + } + } + + private byte[] decrypt(SecretKeySpec cipherKey, SecretKeySpec macKey, byte[] ciphertext) throws InvalidMacException { + try { + if (ciphertext.length < 10) { + throw new InvalidMacException("Ciphertext not long enough for MAC!"); + } + + byte[][] ciphertextParts = ByteUtil.split(ciphertext, ciphertext.length - 10, 10); + + Mac mac = Mac.getInstance("HmacSHA256"); + mac.init(macKey); + + byte[] digest = mac.doFinal(ciphertextParts[0]); + byte[] ourMac = ByteUtil.trim(digest, 10); + byte[] theirMac = ciphertextParts[1]; + + if (!MessageDigest.isEqual(ourMac, theirMac)) { + throw new InvalidMacException("Bad mac!"); + } + + Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); + cipher.init(Cipher.DECRYPT_MODE, cipherKey, new IvParameterSpec(new byte[16])); + + return cipher.doFinal(ciphertextParts[0]); + } catch (NoSuchAlgorithmException | java.security.InvalidKeyException | NoSuchPaddingException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) { + throw new AssertionError(e); + } + } + + + private static class EphemeralKeys { + private final byte[] chainKey; + private final SecretKeySpec cipherKey; + private final SecretKeySpec macKey; + + private EphemeralKeys(byte[] chainKey, byte[] cipherKey, byte[] macKey) { + this.chainKey = chainKey; + this.cipherKey = new SecretKeySpec(cipherKey, "AES"); + this.macKey = new SecretKeySpec(macKey, "HmacSHA256"); + } + } + + private static class StaticKeys { + private final SecretKeySpec cipherKey; + private final SecretKeySpec macKey; + + private StaticKeys(byte[] cipherKey, byte[] macKey) { + this.cipherKey = new SecretKeySpec(cipherKey, "AES"); + this.macKey = new SecretKeySpec(macKey, "HmacSHA256"); + } + } + +} diff --git a/java/src/main/java/org/signal/libsignal/metadata/SelfSendException.java b/java/src/main/java/org/signal/libsignal/metadata/SelfSendException.java new file mode 100644 index 0000000..c3c0c30 --- /dev/null +++ b/java/src/main/java/org/signal/libsignal/metadata/SelfSendException.java @@ -0,0 +1,5 @@ +package org.signal.libsignal.metadata; + + +public class SelfSendException extends Exception { +} diff --git a/java/src/main/java/org/signal/libsignal/metadata/SignalProtos.java b/java/src/main/java/org/signal/libsignal/metadata/SignalProtos.java new file mode 100644 index 0000000..33c298f --- /dev/null +++ b/java/src/main/java/org/signal/libsignal/metadata/SignalProtos.java @@ -0,0 +1,3804 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: UnidentifiedDelivery.proto + +package org.signal.libsignal.metadata; + +public final class SignalProtos { + private SignalProtos() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + } + public interface ServerCertificateOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes certificate = 1; + /** + * optional bytes certificate = 1; + */ + boolean hasCertificate(); + /** + * optional bytes certificate = 1; + */ + com.google.protobuf.ByteString getCertificate(); + + // optional bytes signature = 2; + /** + * optional bytes signature = 2; + */ + boolean hasSignature(); + /** + * optional bytes signature = 2; + */ + com.google.protobuf.ByteString getSignature(); + } + /** + * Protobuf type {@code signal.ServerCertificate} + */ + public static final class ServerCertificate extends + com.google.protobuf.GeneratedMessage + implements ServerCertificateOrBuilder { + // Use ServerCertificate.newBuilder() to construct. + private ServerCertificate(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private ServerCertificate(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final ServerCertificate defaultInstance; + public static ServerCertificate getDefaultInstance() { + return defaultInstance; + } + + public ServerCertificate getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ServerCertificate( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + certificate_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + signature_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_ServerCertificate_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_ServerCertificate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.signal.libsignal.metadata.SignalProtos.ServerCertificate.class, org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public ServerCertificate parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ServerCertificate(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public interface CertificateOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional uint32 id = 1; + /** + * optional uint32 id = 1; + */ + boolean hasId(); + /** + * optional uint32 id = 1; + */ + int getId(); + + // optional bytes key = 2; + /** + * optional bytes key = 2; + */ + boolean hasKey(); + /** + * optional bytes key = 2; + */ + com.google.protobuf.ByteString getKey(); + } + /** + * Protobuf type {@code signal.ServerCertificate.Certificate} + */ + public static final class Certificate extends + com.google.protobuf.GeneratedMessage + implements CertificateOrBuilder { + // Use Certificate.newBuilder() to construct. + private Certificate(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Certificate(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Certificate defaultInstance; + public static Certificate getDefaultInstance() { + return defaultInstance; + } + + public Certificate getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Certificate( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + bitField0_ |= 0x00000001; + id_ = input.readUInt32(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + key_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_ServerCertificate_Certificate_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_ServerCertificate_Certificate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate.class, org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Certificate parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Certificate(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional uint32 id = 1; + public static final int ID_FIELD_NUMBER = 1; + private int id_; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + + // optional bytes key = 2; + public static final int KEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString key_; + /** + * optional bytes key = 2; + */ + public boolean hasKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes key = 2; + */ + public com.google.protobuf.ByteString getKey() { + return key_; + } + + private void initFields() { + id_ = 0; + key_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeUInt32(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, key_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(1, id_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, key_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signal.ServerCertificate.Certificate} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.signal.libsignal.metadata.SignalProtos.ServerCertificate.CertificateOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_ServerCertificate_Certificate_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_ServerCertificate_Certificate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate.class, org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate.Builder.class); + } + + // Construct using org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + id_ = 0; + bitField0_ = (bitField0_ & ~0x00000001); + key_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_ServerCertificate_Certificate_descriptor; + } + + public org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate getDefaultInstanceForType() { + return org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate.getDefaultInstance(); + } + + public org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate build() { + org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate buildPartial() { + org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate result = new org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.id_ = id_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.key_ = key_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate) { + return mergeFrom((org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate other) { + if (other == org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate.getDefaultInstance()) return this; + if (other.hasId()) { + setId(other.getId()); + } + if (other.hasKey()) { + setKey(other.getKey()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Certificate) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional uint32 id = 1; + private int id_ ; + /** + * optional uint32 id = 1; + */ + public boolean hasId() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional uint32 id = 1; + */ + public int getId() { + return id_; + } + /** + * optional uint32 id = 1; + */ + public Builder setId(int value) { + bitField0_ |= 0x00000001; + id_ = value; + onChanged(); + return this; + } + /** + * optional uint32 id = 1; + */ + public Builder clearId() { + bitField0_ = (bitField0_ & ~0x00000001); + id_ = 0; + onChanged(); + return this; + } + + // optional bytes key = 2; + private com.google.protobuf.ByteString key_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes key = 2; + */ + public boolean hasKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes key = 2; + */ + public com.google.protobuf.ByteString getKey() { + return key_; + } + /** + * optional bytes key = 2; + */ + public Builder setKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + key_ = value; + onChanged(); + return this; + } + /** + * optional bytes key = 2; + */ + public Builder clearKey() { + bitField0_ = (bitField0_ & ~0x00000002); + key_ = getDefaultInstance().getKey(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signal.ServerCertificate.Certificate) + } + + static { + defaultInstance = new Certificate(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signal.ServerCertificate.Certificate) + } + + private int bitField0_; + // optional bytes certificate = 1; + public static final int CERTIFICATE_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString certificate_; + /** + * optional bytes certificate = 1; + */ + public boolean hasCertificate() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes certificate = 1; + */ + public com.google.protobuf.ByteString getCertificate() { + return certificate_; + } + + // optional bytes signature = 2; + public static final int SIGNATURE_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString signature_; + /** + * optional bytes signature = 2; + */ + public boolean hasSignature() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes signature = 2; + */ + public com.google.protobuf.ByteString getSignature() { + return signature_; + } + + private void initFields() { + certificate_ = com.google.protobuf.ByteString.EMPTY; + signature_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, certificate_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, signature_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, certificate_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, signature_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.signal.libsignal.metadata.SignalProtos.ServerCertificate parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.signal.libsignal.metadata.SignalProtos.ServerCertificate prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signal.ServerCertificate} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.signal.libsignal.metadata.SignalProtos.ServerCertificateOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_ServerCertificate_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_ServerCertificate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.signal.libsignal.metadata.SignalProtos.ServerCertificate.class, org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Builder.class); + } + + // Construct using org.signal.libsignal.metadata.SignalProtos.ServerCertificate.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + certificate_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + signature_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_ServerCertificate_descriptor; + } + + public org.signal.libsignal.metadata.SignalProtos.ServerCertificate getDefaultInstanceForType() { + return org.signal.libsignal.metadata.SignalProtos.ServerCertificate.getDefaultInstance(); + } + + public org.signal.libsignal.metadata.SignalProtos.ServerCertificate build() { + org.signal.libsignal.metadata.SignalProtos.ServerCertificate result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.signal.libsignal.metadata.SignalProtos.ServerCertificate buildPartial() { + org.signal.libsignal.metadata.SignalProtos.ServerCertificate result = new org.signal.libsignal.metadata.SignalProtos.ServerCertificate(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.certificate_ = certificate_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.signature_ = signature_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.signal.libsignal.metadata.SignalProtos.ServerCertificate) { + return mergeFrom((org.signal.libsignal.metadata.SignalProtos.ServerCertificate)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.signal.libsignal.metadata.SignalProtos.ServerCertificate other) { + if (other == org.signal.libsignal.metadata.SignalProtos.ServerCertificate.getDefaultInstance()) return this; + if (other.hasCertificate()) { + setCertificate(other.getCertificate()); + } + if (other.hasSignature()) { + setSignature(other.getSignature()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.signal.libsignal.metadata.SignalProtos.ServerCertificate parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.signal.libsignal.metadata.SignalProtos.ServerCertificate) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes certificate = 1; + private com.google.protobuf.ByteString certificate_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes certificate = 1; + */ + public boolean hasCertificate() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes certificate = 1; + */ + public com.google.protobuf.ByteString getCertificate() { + return certificate_; + } + /** + * optional bytes certificate = 1; + */ + public Builder setCertificate(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + certificate_ = value; + onChanged(); + return this; + } + /** + * optional bytes certificate = 1; + */ + public Builder clearCertificate() { + bitField0_ = (bitField0_ & ~0x00000001); + certificate_ = getDefaultInstance().getCertificate(); + onChanged(); + return this; + } + + // optional bytes signature = 2; + private com.google.protobuf.ByteString signature_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes signature = 2; + */ + public boolean hasSignature() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes signature = 2; + */ + public com.google.protobuf.ByteString getSignature() { + return signature_; + } + /** + * optional bytes signature = 2; + */ + public Builder setSignature(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + signature_ = value; + onChanged(); + return this; + } + /** + * optional bytes signature = 2; + */ + public Builder clearSignature() { + bitField0_ = (bitField0_ & ~0x00000002); + signature_ = getDefaultInstance().getSignature(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signal.ServerCertificate) + } + + static { + defaultInstance = new ServerCertificate(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signal.ServerCertificate) + } + + public interface SenderCertificateOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes certificate = 1; + /** + * optional bytes certificate = 1; + */ + boolean hasCertificate(); + /** + * optional bytes certificate = 1; + */ + com.google.protobuf.ByteString getCertificate(); + + // optional bytes signature = 2; + /** + * optional bytes signature = 2; + */ + boolean hasSignature(); + /** + * optional bytes signature = 2; + */ + com.google.protobuf.ByteString getSignature(); + } + /** + * Protobuf type {@code signal.SenderCertificate} + */ + public static final class SenderCertificate extends + com.google.protobuf.GeneratedMessage + implements SenderCertificateOrBuilder { + // Use SenderCertificate.newBuilder() to construct. + private SenderCertificate(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private SenderCertificate(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final SenderCertificate defaultInstance; + public static SenderCertificate getDefaultInstance() { + return defaultInstance; + } + + public SenderCertificate getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private SenderCertificate( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + certificate_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + signature_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_SenderCertificate_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_SenderCertificate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.signal.libsignal.metadata.SignalProtos.SenderCertificate.class, org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public SenderCertificate parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new SenderCertificate(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public interface CertificateOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional string sender = 1; + /** + * optional string sender = 1; + */ + boolean hasSender(); + /** + * optional string sender = 1; + */ + java.lang.String getSender(); + /** + * optional string sender = 1; + */ + com.google.protobuf.ByteString + getSenderBytes(); + + // optional uint32 senderDevice = 2; + /** + * optional uint32 senderDevice = 2; + */ + boolean hasSenderDevice(); + /** + * optional uint32 senderDevice = 2; + */ + int getSenderDevice(); + + // optional fixed64 expires = 3; + /** + * optional fixed64 expires = 3; + */ + boolean hasExpires(); + /** + * optional fixed64 expires = 3; + */ + long getExpires(); + + // optional bytes identityKey = 4; + /** + * optional bytes identityKey = 4; + */ + boolean hasIdentityKey(); + /** + * optional bytes identityKey = 4; + */ + com.google.protobuf.ByteString getIdentityKey(); + + // optional .signal.ServerCertificate signer = 5; + /** + * optional .signal.ServerCertificate signer = 5; + */ + boolean hasSigner(); + /** + * optional .signal.ServerCertificate signer = 5; + */ + org.signal.libsignal.metadata.SignalProtos.ServerCertificate getSigner(); + /** + * optional .signal.ServerCertificate signer = 5; + */ + org.signal.libsignal.metadata.SignalProtos.ServerCertificateOrBuilder getSignerOrBuilder(); + } + /** + * Protobuf type {@code signal.SenderCertificate.Certificate} + */ + public static final class Certificate extends + com.google.protobuf.GeneratedMessage + implements CertificateOrBuilder { + // Use Certificate.newBuilder() to construct. + private Certificate(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Certificate(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Certificate defaultInstance; + public static Certificate getDefaultInstance() { + return defaultInstance; + } + + public Certificate getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Certificate( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + sender_ = input.readBytes(); + break; + } + case 16: { + bitField0_ |= 0x00000002; + senderDevice_ = input.readUInt32(); + break; + } + case 25: { + bitField0_ |= 0x00000004; + expires_ = input.readFixed64(); + break; + } + case 34: { + bitField0_ |= 0x00000008; + identityKey_ = input.readBytes(); + break; + } + case 42: { + org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Builder subBuilder = null; + if (((bitField0_ & 0x00000010) == 0x00000010)) { + subBuilder = signer_.toBuilder(); + } + signer_ = input.readMessage(org.signal.libsignal.metadata.SignalProtos.ServerCertificate.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(signer_); + signer_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000010; + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_SenderCertificate_Certificate_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_SenderCertificate_Certificate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate.class, org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Certificate parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Certificate(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + private int bitField0_; + // optional string sender = 1; + public static final int SENDER_FIELD_NUMBER = 1; + private java.lang.Object sender_; + /** + * optional string sender = 1; + */ + public boolean hasSender() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string sender = 1; + */ + public java.lang.String getSender() { + java.lang.Object ref = sender_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + if (bs.isValidUtf8()) { + sender_ = s; + } + return s; + } + } + /** + * optional string sender = 1; + */ + public com.google.protobuf.ByteString + getSenderBytes() { + java.lang.Object ref = sender_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + sender_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + // optional uint32 senderDevice = 2; + public static final int SENDERDEVICE_FIELD_NUMBER = 2; + private int senderDevice_; + /** + * optional uint32 senderDevice = 2; + */ + public boolean hasSenderDevice() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 senderDevice = 2; + */ + public int getSenderDevice() { + return senderDevice_; + } + + // optional fixed64 expires = 3; + public static final int EXPIRES_FIELD_NUMBER = 3; + private long expires_; + /** + * optional fixed64 expires = 3; + */ + public boolean hasExpires() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional fixed64 expires = 3; + */ + public long getExpires() { + return expires_; + } + + // optional bytes identityKey = 4; + public static final int IDENTITYKEY_FIELD_NUMBER = 4; + private com.google.protobuf.ByteString identityKey_; + /** + * optional bytes identityKey = 4; + */ + public boolean hasIdentityKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes identityKey = 4; + */ + public com.google.protobuf.ByteString getIdentityKey() { + return identityKey_; + } + + // optional .signal.ServerCertificate signer = 5; + public static final int SIGNER_FIELD_NUMBER = 5; + private org.signal.libsignal.metadata.SignalProtos.ServerCertificate signer_; + /** + * optional .signal.ServerCertificate signer = 5; + */ + public boolean hasSigner() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional .signal.ServerCertificate signer = 5; + */ + public org.signal.libsignal.metadata.SignalProtos.ServerCertificate getSigner() { + return signer_; + } + /** + * optional .signal.ServerCertificate signer = 5; + */ + public org.signal.libsignal.metadata.SignalProtos.ServerCertificateOrBuilder getSignerOrBuilder() { + return signer_; + } + + private void initFields() { + sender_ = ""; + senderDevice_ = 0; + expires_ = 0L; + identityKey_ = com.google.protobuf.ByteString.EMPTY; + signer_ = org.signal.libsignal.metadata.SignalProtos.ServerCertificate.getDefaultInstance(); + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, getSenderBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeUInt32(2, senderDevice_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeFixed64(3, expires_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + output.writeBytes(4, identityKey_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeMessage(5, signer_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, getSenderBytes()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeUInt32Size(2, senderDevice_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeFixed64Size(3, expires_); + } + if (((bitField0_ & 0x00000008) == 0x00000008)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(4, identityKey_); + } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(5, signer_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signal.SenderCertificate.Certificate} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.signal.libsignal.metadata.SignalProtos.SenderCertificate.CertificateOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_SenderCertificate_Certificate_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_SenderCertificate_Certificate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate.class, org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate.Builder.class); + } + + // Construct using org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getSignerFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + sender_ = ""; + bitField0_ = (bitField0_ & ~0x00000001); + senderDevice_ = 0; + bitField0_ = (bitField0_ & ~0x00000002); + expires_ = 0L; + bitField0_ = (bitField0_ & ~0x00000004); + identityKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000008); + if (signerBuilder_ == null) { + signer_ = org.signal.libsignal.metadata.SignalProtos.ServerCertificate.getDefaultInstance(); + } else { + signerBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000010); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_SenderCertificate_Certificate_descriptor; + } + + public org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate getDefaultInstanceForType() { + return org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate.getDefaultInstance(); + } + + public org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate build() { + org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate buildPartial() { + org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate result = new org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.sender_ = sender_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.senderDevice_ = senderDevice_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.expires_ = expires_; + if (((from_bitField0_ & 0x00000008) == 0x00000008)) { + to_bitField0_ |= 0x00000008; + } + result.identityKey_ = identityKey_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + if (signerBuilder_ == null) { + result.signer_ = signer_; + } else { + result.signer_ = signerBuilder_.build(); + } + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate) { + return mergeFrom((org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate other) { + if (other == org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate.getDefaultInstance()) return this; + if (other.hasSender()) { + bitField0_ |= 0x00000001; + sender_ = other.sender_; + onChanged(); + } + if (other.hasSenderDevice()) { + setSenderDevice(other.getSenderDevice()); + } + if (other.hasExpires()) { + setExpires(other.getExpires()); + } + if (other.hasIdentityKey()) { + setIdentityKey(other.getIdentityKey()); + } + if (other.hasSigner()) { + mergeSigner(other.getSigner()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Certificate) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional string sender = 1; + private java.lang.Object sender_ = ""; + /** + * optional string sender = 1; + */ + public boolean hasSender() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional string sender = 1; + */ + public java.lang.String getSender() { + java.lang.Object ref = sender_; + if (!(ref instanceof java.lang.String)) { + java.lang.String s = ((com.google.protobuf.ByteString) ref) + .toStringUtf8(); + sender_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * optional string sender = 1; + */ + public com.google.protobuf.ByteString + getSenderBytes() { + java.lang.Object ref = sender_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + sender_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * optional string sender = 1; + */ + public Builder setSender( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + sender_ = value; + onChanged(); + return this; + } + /** + * optional string sender = 1; + */ + public Builder clearSender() { + bitField0_ = (bitField0_ & ~0x00000001); + sender_ = getDefaultInstance().getSender(); + onChanged(); + return this; + } + /** + * optional string sender = 1; + */ + public Builder setSenderBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + sender_ = value; + onChanged(); + return this; + } + + // optional uint32 senderDevice = 2; + private int senderDevice_ ; + /** + * optional uint32 senderDevice = 2; + */ + public boolean hasSenderDevice() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional uint32 senderDevice = 2; + */ + public int getSenderDevice() { + return senderDevice_; + } + /** + * optional uint32 senderDevice = 2; + */ + public Builder setSenderDevice(int value) { + bitField0_ |= 0x00000002; + senderDevice_ = value; + onChanged(); + return this; + } + /** + * optional uint32 senderDevice = 2; + */ + public Builder clearSenderDevice() { + bitField0_ = (bitField0_ & ~0x00000002); + senderDevice_ = 0; + onChanged(); + return this; + } + + // optional fixed64 expires = 3; + private long expires_ ; + /** + * optional fixed64 expires = 3; + */ + public boolean hasExpires() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional fixed64 expires = 3; + */ + public long getExpires() { + return expires_; + } + /** + * optional fixed64 expires = 3; + */ + public Builder setExpires(long value) { + bitField0_ |= 0x00000004; + expires_ = value; + onChanged(); + return this; + } + /** + * optional fixed64 expires = 3; + */ + public Builder clearExpires() { + bitField0_ = (bitField0_ & ~0x00000004); + expires_ = 0L; + onChanged(); + return this; + } + + // optional bytes identityKey = 4; + private com.google.protobuf.ByteString identityKey_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes identityKey = 4; + */ + public boolean hasIdentityKey() { + return ((bitField0_ & 0x00000008) == 0x00000008); + } + /** + * optional bytes identityKey = 4; + */ + public com.google.protobuf.ByteString getIdentityKey() { + return identityKey_; + } + /** + * optional bytes identityKey = 4; + */ + public Builder setIdentityKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000008; + identityKey_ = value; + onChanged(); + return this; + } + /** + * optional bytes identityKey = 4; + */ + public Builder clearIdentityKey() { + bitField0_ = (bitField0_ & ~0x00000008); + identityKey_ = getDefaultInstance().getIdentityKey(); + onChanged(); + return this; + } + + // optional .signal.ServerCertificate signer = 5; + private org.signal.libsignal.metadata.SignalProtos.ServerCertificate signer_ = org.signal.libsignal.metadata.SignalProtos.ServerCertificate.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.signal.libsignal.metadata.SignalProtos.ServerCertificate, org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Builder, org.signal.libsignal.metadata.SignalProtos.ServerCertificateOrBuilder> signerBuilder_; + /** + * optional .signal.ServerCertificate signer = 5; + */ + public boolean hasSigner() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional .signal.ServerCertificate signer = 5; + */ + public org.signal.libsignal.metadata.SignalProtos.ServerCertificate getSigner() { + if (signerBuilder_ == null) { + return signer_; + } else { + return signerBuilder_.getMessage(); + } + } + /** + * optional .signal.ServerCertificate signer = 5; + */ + public Builder setSigner(org.signal.libsignal.metadata.SignalProtos.ServerCertificate value) { + if (signerBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + signer_ = value; + onChanged(); + } else { + signerBuilder_.setMessage(value); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .signal.ServerCertificate signer = 5; + */ + public Builder setSigner( + org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Builder builderForValue) { + if (signerBuilder_ == null) { + signer_ = builderForValue.build(); + onChanged(); + } else { + signerBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .signal.ServerCertificate signer = 5; + */ + public Builder mergeSigner(org.signal.libsignal.metadata.SignalProtos.ServerCertificate value) { + if (signerBuilder_ == null) { + if (((bitField0_ & 0x00000010) == 0x00000010) && + signer_ != org.signal.libsignal.metadata.SignalProtos.ServerCertificate.getDefaultInstance()) { + signer_ = + org.signal.libsignal.metadata.SignalProtos.ServerCertificate.newBuilder(signer_).mergeFrom(value).buildPartial(); + } else { + signer_ = value; + } + onChanged(); + } else { + signerBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000010; + return this; + } + /** + * optional .signal.ServerCertificate signer = 5; + */ + public Builder clearSigner() { + if (signerBuilder_ == null) { + signer_ = org.signal.libsignal.metadata.SignalProtos.ServerCertificate.getDefaultInstance(); + onChanged(); + } else { + signerBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000010); + return this; + } + /** + * optional .signal.ServerCertificate signer = 5; + */ + public org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Builder getSignerBuilder() { + bitField0_ |= 0x00000010; + onChanged(); + return getSignerFieldBuilder().getBuilder(); + } + /** + * optional .signal.ServerCertificate signer = 5; + */ + public org.signal.libsignal.metadata.SignalProtos.ServerCertificateOrBuilder getSignerOrBuilder() { + if (signerBuilder_ != null) { + return signerBuilder_.getMessageOrBuilder(); + } else { + return signer_; + } + } + /** + * optional .signal.ServerCertificate signer = 5; + */ + private com.google.protobuf.SingleFieldBuilder< + org.signal.libsignal.metadata.SignalProtos.ServerCertificate, org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Builder, org.signal.libsignal.metadata.SignalProtos.ServerCertificateOrBuilder> + getSignerFieldBuilder() { + if (signerBuilder_ == null) { + signerBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.signal.libsignal.metadata.SignalProtos.ServerCertificate, org.signal.libsignal.metadata.SignalProtos.ServerCertificate.Builder, org.signal.libsignal.metadata.SignalProtos.ServerCertificateOrBuilder>( + signer_, + getParentForChildren(), + isClean()); + signer_ = null; + } + return signerBuilder_; + } + + // @@protoc_insertion_point(builder_scope:signal.SenderCertificate.Certificate) + } + + static { + defaultInstance = new Certificate(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signal.SenderCertificate.Certificate) + } + + private int bitField0_; + // optional bytes certificate = 1; + public static final int CERTIFICATE_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString certificate_; + /** + * optional bytes certificate = 1; + */ + public boolean hasCertificate() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes certificate = 1; + */ + public com.google.protobuf.ByteString getCertificate() { + return certificate_; + } + + // optional bytes signature = 2; + public static final int SIGNATURE_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString signature_; + /** + * optional bytes signature = 2; + */ + public boolean hasSignature() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes signature = 2; + */ + public com.google.protobuf.ByteString getSignature() { + return signature_; + } + + private void initFields() { + certificate_ = com.google.protobuf.ByteString.EMPTY; + signature_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, certificate_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, signature_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, certificate_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, signature_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.signal.libsignal.metadata.SignalProtos.SenderCertificate parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.signal.libsignal.metadata.SignalProtos.SenderCertificate prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signal.SenderCertificate} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.signal.libsignal.metadata.SignalProtos.SenderCertificateOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_SenderCertificate_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_SenderCertificate_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.signal.libsignal.metadata.SignalProtos.SenderCertificate.class, org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Builder.class); + } + + // Construct using org.signal.libsignal.metadata.SignalProtos.SenderCertificate.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + certificate_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + signature_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_SenderCertificate_descriptor; + } + + public org.signal.libsignal.metadata.SignalProtos.SenderCertificate getDefaultInstanceForType() { + return org.signal.libsignal.metadata.SignalProtos.SenderCertificate.getDefaultInstance(); + } + + public org.signal.libsignal.metadata.SignalProtos.SenderCertificate build() { + org.signal.libsignal.metadata.SignalProtos.SenderCertificate result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.signal.libsignal.metadata.SignalProtos.SenderCertificate buildPartial() { + org.signal.libsignal.metadata.SignalProtos.SenderCertificate result = new org.signal.libsignal.metadata.SignalProtos.SenderCertificate(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.certificate_ = certificate_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.signature_ = signature_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.signal.libsignal.metadata.SignalProtos.SenderCertificate) { + return mergeFrom((org.signal.libsignal.metadata.SignalProtos.SenderCertificate)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.signal.libsignal.metadata.SignalProtos.SenderCertificate other) { + if (other == org.signal.libsignal.metadata.SignalProtos.SenderCertificate.getDefaultInstance()) return this; + if (other.hasCertificate()) { + setCertificate(other.getCertificate()); + } + if (other.hasSignature()) { + setSignature(other.getSignature()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.signal.libsignal.metadata.SignalProtos.SenderCertificate parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.signal.libsignal.metadata.SignalProtos.SenderCertificate) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes certificate = 1; + private com.google.protobuf.ByteString certificate_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes certificate = 1; + */ + public boolean hasCertificate() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes certificate = 1; + */ + public com.google.protobuf.ByteString getCertificate() { + return certificate_; + } + /** + * optional bytes certificate = 1; + */ + public Builder setCertificate(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + certificate_ = value; + onChanged(); + return this; + } + /** + * optional bytes certificate = 1; + */ + public Builder clearCertificate() { + bitField0_ = (bitField0_ & ~0x00000001); + certificate_ = getDefaultInstance().getCertificate(); + onChanged(); + return this; + } + + // optional bytes signature = 2; + private com.google.protobuf.ByteString signature_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes signature = 2; + */ + public boolean hasSignature() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes signature = 2; + */ + public com.google.protobuf.ByteString getSignature() { + return signature_; + } + /** + * optional bytes signature = 2; + */ + public Builder setSignature(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + signature_ = value; + onChanged(); + return this; + } + /** + * optional bytes signature = 2; + */ + public Builder clearSignature() { + bitField0_ = (bitField0_ & ~0x00000002); + signature_ = getDefaultInstance().getSignature(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signal.SenderCertificate) + } + + static { + defaultInstance = new SenderCertificate(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signal.SenderCertificate) + } + + public interface UnidentifiedSenderMessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes ephemeralPublic = 1; + /** + * optional bytes ephemeralPublic = 1; + */ + boolean hasEphemeralPublic(); + /** + * optional bytes ephemeralPublic = 1; + */ + com.google.protobuf.ByteString getEphemeralPublic(); + + // optional bytes encryptedStatic = 2; + /** + * optional bytes encryptedStatic = 2; + */ + boolean hasEncryptedStatic(); + /** + * optional bytes encryptedStatic = 2; + */ + com.google.protobuf.ByteString getEncryptedStatic(); + + // optional bytes encryptedMessage = 3; + /** + * optional bytes encryptedMessage = 3; + */ + boolean hasEncryptedMessage(); + /** + * optional bytes encryptedMessage = 3; + */ + com.google.protobuf.ByteString getEncryptedMessage(); + } + /** + * Protobuf type {@code signal.UnidentifiedSenderMessage} + */ + public static final class UnidentifiedSenderMessage extends + com.google.protobuf.GeneratedMessage + implements UnidentifiedSenderMessageOrBuilder { + // Use UnidentifiedSenderMessage.newBuilder() to construct. + private UnidentifiedSenderMessage(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private UnidentifiedSenderMessage(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final UnidentifiedSenderMessage defaultInstance; + public static UnidentifiedSenderMessage getDefaultInstance() { + return defaultInstance; + } + + public UnidentifiedSenderMessage getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private UnidentifiedSenderMessage( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + ephemeralPublic_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + encryptedStatic_ = input.readBytes(); + break; + } + case 26: { + bitField0_ |= 0x00000004; + encryptedMessage_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_UnidentifiedSenderMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_UnidentifiedSenderMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.class, org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public UnidentifiedSenderMessage parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new UnidentifiedSenderMessage(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + public interface MessageOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + /** + * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + */ + boolean hasType(); + /** + * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + */ + org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type getType(); + + // optional .signal.SenderCertificate senderCertificate = 2; + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + boolean hasSenderCertificate(); + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + org.signal.libsignal.metadata.SignalProtos.SenderCertificate getSenderCertificate(); + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + org.signal.libsignal.metadata.SignalProtos.SenderCertificateOrBuilder getSenderCertificateOrBuilder(); + + // optional bytes content = 3; + /** + * optional bytes content = 3; + */ + boolean hasContent(); + /** + * optional bytes content = 3; + */ + com.google.protobuf.ByteString getContent(); + } + /** + * Protobuf type {@code signal.UnidentifiedSenderMessage.Message} + */ + public static final class Message extends + com.google.protobuf.GeneratedMessage + implements MessageOrBuilder { + // Use Message.newBuilder() to construct. + private Message(com.google.protobuf.GeneratedMessage.Builder builder) { + super(builder); + this.unknownFields = builder.getUnknownFields(); + } + private Message(boolean noInit) { this.unknownFields = com.google.protobuf.UnknownFieldSet.getDefaultInstance(); } + + private static final Message defaultInstance; + public static Message getDefaultInstance() { + return defaultInstance; + } + + public Message getDefaultInstanceForType() { + return defaultInstance; + } + + private final com.google.protobuf.UnknownFieldSet unknownFields; + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private Message( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + initFields(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + done = true; + } + break; + } + case 8: { + int rawValue = input.readEnum(); + org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type value = org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type.valueOf(rawValue); + if (value == null) { + unknownFields.mergeVarintField(1, rawValue); + } else { + bitField0_ |= 0x00000001; + type_ = value; + } + break; + } + case 18: { + org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Builder subBuilder = null; + if (((bitField0_ & 0x00000002) == 0x00000002)) { + subBuilder = senderCertificate_.toBuilder(); + } + senderCertificate_ = input.readMessage(org.signal.libsignal.metadata.SignalProtos.SenderCertificate.PARSER, extensionRegistry); + if (subBuilder != null) { + subBuilder.mergeFrom(senderCertificate_); + senderCertificate_ = subBuilder.buildPartial(); + } + bitField0_ |= 0x00000002; + break; + } + case 26: { + bitField0_ |= 0x00000004; + content_ = input.readBytes(); + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e.getMessage()).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_UnidentifiedSenderMessage_Message_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_UnidentifiedSenderMessage_Message_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.class, org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Builder.class); + } + + public static com.google.protobuf.Parser PARSER = + new com.google.protobuf.AbstractParser() { + public Message parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Message(input, extensionRegistry); + } + }; + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + /** + * Protobuf enum {@code signal.UnidentifiedSenderMessage.Message.Type} + */ + public enum Type + implements com.google.protobuf.ProtocolMessageEnum { + /** + * PREKEY_MESSAGE = 1; + */ + PREKEY_MESSAGE(0, 1), + /** + * MESSAGE = 2; + */ + MESSAGE(1, 2), + ; + + /** + * PREKEY_MESSAGE = 1; + */ + public static final int PREKEY_MESSAGE_VALUE = 1; + /** + * MESSAGE = 2; + */ + public static final int MESSAGE_VALUE = 2; + + + public final int getNumber() { return value; } + + public static Type valueOf(int value) { + switch (value) { + case 1: return PREKEY_MESSAGE; + case 2: return MESSAGE; + default: return null; + } + } + + public static com.google.protobuf.Internal.EnumLiteMap + internalGetValueMap() { + return internalValueMap; + } + private static com.google.protobuf.Internal.EnumLiteMap + internalValueMap = + new com.google.protobuf.Internal.EnumLiteMap() { + public Type findValueByNumber(int number) { + return Type.valueOf(number); + } + }; + + public final com.google.protobuf.Descriptors.EnumValueDescriptor + getValueDescriptor() { + return getDescriptor().getValues().get(index); + } + public final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptorForType() { + return getDescriptor(); + } + public static final com.google.protobuf.Descriptors.EnumDescriptor + getDescriptor() { + return org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.getDescriptor().getEnumTypes().get(0); + } + + private static final Type[] VALUES = values(); + + public static Type valueOf( + com.google.protobuf.Descriptors.EnumValueDescriptor desc) { + if (desc.getType() != getDescriptor()) { + throw new java.lang.IllegalArgumentException( + "EnumValueDescriptor is not for this type."); + } + return VALUES[desc.getIndex()]; + } + + private final int index; + private final int value; + + private Type(int index, int value) { + this.index = index; + this.value = value; + } + + // @@protoc_insertion_point(enum_scope:signal.UnidentifiedSenderMessage.Message.Type) + } + + private int bitField0_; + // optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + public static final int TYPE_FIELD_NUMBER = 1; + private org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type type_; + /** + * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + */ + public org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type getType() { + return type_; + } + + // optional .signal.SenderCertificate senderCertificate = 2; + public static final int SENDERCERTIFICATE_FIELD_NUMBER = 2; + private org.signal.libsignal.metadata.SignalProtos.SenderCertificate senderCertificate_; + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public boolean hasSenderCertificate() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public org.signal.libsignal.metadata.SignalProtos.SenderCertificate getSenderCertificate() { + return senderCertificate_; + } + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public org.signal.libsignal.metadata.SignalProtos.SenderCertificateOrBuilder getSenderCertificateOrBuilder() { + return senderCertificate_; + } + + // optional bytes content = 3; + public static final int CONTENT_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString content_; + /** + * optional bytes content = 3; + */ + public boolean hasContent() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes content = 3; + */ + public com.google.protobuf.ByteString getContent() { + return content_; + } + + private void initFields() { + type_ = org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type.PREKEY_MESSAGE; + senderCertificate_ = org.signal.libsignal.metadata.SignalProtos.SenderCertificate.getDefaultInstance(); + content_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeEnum(1, type_.getNumber()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeMessage(2, senderCertificate_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, content_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeEnumSize(1, type_.getNumber()); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, senderCertificate_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, content_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signal.UnidentifiedSenderMessage.Message} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.MessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_UnidentifiedSenderMessage_Message_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_UnidentifiedSenderMessage_Message_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.class, org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Builder.class); + } + + // Construct using org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + getSenderCertificateFieldBuilder(); + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + type_ = org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type.PREKEY_MESSAGE; + bitField0_ = (bitField0_ & ~0x00000001); + if (senderCertificateBuilder_ == null) { + senderCertificate_ = org.signal.libsignal.metadata.SignalProtos.SenderCertificate.getDefaultInstance(); + } else { + senderCertificateBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + content_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_UnidentifiedSenderMessage_Message_descriptor; + } + + public org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message getDefaultInstanceForType() { + return org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.getDefaultInstance(); + } + + public org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message build() { + org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message buildPartial() { + org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message result = new org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.type_ = type_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + if (senderCertificateBuilder_ == null) { + result.senderCertificate_ = senderCertificate_; + } else { + result.senderCertificate_ = senderCertificateBuilder_.build(); + } + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.content_ = content_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message) { + return mergeFrom((org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message other) { + if (other == org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.getDefaultInstance()) return this; + if (other.hasType()) { + setType(other.getType()); + } + if (other.hasSenderCertificate()) { + mergeSenderCertificate(other.getSenderCertificate()); + } + if (other.hasContent()) { + setContent(other.getContent()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + private org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type type_ = org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type.PREKEY_MESSAGE; + /** + * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + */ + public boolean hasType() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + */ + public org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type getType() { + return type_; + } + /** + * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + */ + public Builder setType(org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + type_ = value; + onChanged(); + return this; + } + /** + * optional .signal.UnidentifiedSenderMessage.Message.Type type = 1; + */ + public Builder clearType() { + bitField0_ = (bitField0_ & ~0x00000001); + type_ = org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Message.Type.PREKEY_MESSAGE; + onChanged(); + return this; + } + + // optional .signal.SenderCertificate senderCertificate = 2; + private org.signal.libsignal.metadata.SignalProtos.SenderCertificate senderCertificate_ = org.signal.libsignal.metadata.SignalProtos.SenderCertificate.getDefaultInstance(); + private com.google.protobuf.SingleFieldBuilder< + org.signal.libsignal.metadata.SignalProtos.SenderCertificate, org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Builder, org.signal.libsignal.metadata.SignalProtos.SenderCertificateOrBuilder> senderCertificateBuilder_; + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public boolean hasSenderCertificate() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public org.signal.libsignal.metadata.SignalProtos.SenderCertificate getSenderCertificate() { + if (senderCertificateBuilder_ == null) { + return senderCertificate_; + } else { + return senderCertificateBuilder_.getMessage(); + } + } + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public Builder setSenderCertificate(org.signal.libsignal.metadata.SignalProtos.SenderCertificate value) { + if (senderCertificateBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + senderCertificate_ = value; + onChanged(); + } else { + senderCertificateBuilder_.setMessage(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public Builder setSenderCertificate( + org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Builder builderForValue) { + if (senderCertificateBuilder_ == null) { + senderCertificate_ = builderForValue.build(); + onChanged(); + } else { + senderCertificateBuilder_.setMessage(builderForValue.build()); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public Builder mergeSenderCertificate(org.signal.libsignal.metadata.SignalProtos.SenderCertificate value) { + if (senderCertificateBuilder_ == null) { + if (((bitField0_ & 0x00000002) == 0x00000002) && + senderCertificate_ != org.signal.libsignal.metadata.SignalProtos.SenderCertificate.getDefaultInstance()) { + senderCertificate_ = + org.signal.libsignal.metadata.SignalProtos.SenderCertificate.newBuilder(senderCertificate_).mergeFrom(value).buildPartial(); + } else { + senderCertificate_ = value; + } + onChanged(); + } else { + senderCertificateBuilder_.mergeFrom(value); + } + bitField0_ |= 0x00000002; + return this; + } + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public Builder clearSenderCertificate() { + if (senderCertificateBuilder_ == null) { + senderCertificate_ = org.signal.libsignal.metadata.SignalProtos.SenderCertificate.getDefaultInstance(); + onChanged(); + } else { + senderCertificateBuilder_.clear(); + } + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Builder getSenderCertificateBuilder() { + bitField0_ |= 0x00000002; + onChanged(); + return getSenderCertificateFieldBuilder().getBuilder(); + } + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + public org.signal.libsignal.metadata.SignalProtos.SenderCertificateOrBuilder getSenderCertificateOrBuilder() { + if (senderCertificateBuilder_ != null) { + return senderCertificateBuilder_.getMessageOrBuilder(); + } else { + return senderCertificate_; + } + } + /** + * optional .signal.SenderCertificate senderCertificate = 2; + */ + private com.google.protobuf.SingleFieldBuilder< + org.signal.libsignal.metadata.SignalProtos.SenderCertificate, org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Builder, org.signal.libsignal.metadata.SignalProtos.SenderCertificateOrBuilder> + getSenderCertificateFieldBuilder() { + if (senderCertificateBuilder_ == null) { + senderCertificateBuilder_ = new com.google.protobuf.SingleFieldBuilder< + org.signal.libsignal.metadata.SignalProtos.SenderCertificate, org.signal.libsignal.metadata.SignalProtos.SenderCertificate.Builder, org.signal.libsignal.metadata.SignalProtos.SenderCertificateOrBuilder>( + senderCertificate_, + getParentForChildren(), + isClean()); + senderCertificate_ = null; + } + return senderCertificateBuilder_; + } + + // optional bytes content = 3; + private com.google.protobuf.ByteString content_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes content = 3; + */ + public boolean hasContent() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes content = 3; + */ + public com.google.protobuf.ByteString getContent() { + return content_; + } + /** + * optional bytes content = 3; + */ + public Builder setContent(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + content_ = value; + onChanged(); + return this; + } + /** + * optional bytes content = 3; + */ + public Builder clearContent() { + bitField0_ = (bitField0_ & ~0x00000004); + content_ = getDefaultInstance().getContent(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signal.UnidentifiedSenderMessage.Message) + } + + static { + defaultInstance = new Message(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signal.UnidentifiedSenderMessage.Message) + } + + private int bitField0_; + // optional bytes ephemeralPublic = 1; + public static final int EPHEMERALPUBLIC_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString ephemeralPublic_; + /** + * optional bytes ephemeralPublic = 1; + */ + public boolean hasEphemeralPublic() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes ephemeralPublic = 1; + */ + public com.google.protobuf.ByteString getEphemeralPublic() { + return ephemeralPublic_; + } + + // optional bytes encryptedStatic = 2; + public static final int ENCRYPTEDSTATIC_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString encryptedStatic_; + /** + * optional bytes encryptedStatic = 2; + */ + public boolean hasEncryptedStatic() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes encryptedStatic = 2; + */ + public com.google.protobuf.ByteString getEncryptedStatic() { + return encryptedStatic_; + } + + // optional bytes encryptedMessage = 3; + public static final int ENCRYPTEDMESSAGE_FIELD_NUMBER = 3; + private com.google.protobuf.ByteString encryptedMessage_; + /** + * optional bytes encryptedMessage = 3; + */ + public boolean hasEncryptedMessage() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes encryptedMessage = 3; + */ + public com.google.protobuf.ByteString getEncryptedMessage() { + return encryptedMessage_; + } + + private void initFields() { + ephemeralPublic_ = com.google.protobuf.ByteString.EMPTY; + encryptedStatic_ = com.google.protobuf.ByteString.EMPTY; + encryptedMessage_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, ephemeralPublic_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, encryptedStatic_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + output.writeBytes(3, encryptedMessage_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, ephemeralPublic_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, encryptedStatic_); + } + if (((bitField0_ & 0x00000004) == 0x00000004)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(3, encryptedMessage_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parseFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input); + } + public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseDelimitedFrom(input, extensionRegistry); + } + public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return PARSER.parseFrom(input); + } + public static org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return PARSER.parseFrom(input, extensionRegistry); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + * Protobuf type {@code signal.UnidentifiedSenderMessage} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessageOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_UnidentifiedSenderMessage_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_UnidentifiedSenderMessage_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.class, org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.Builder.class); + } + + // Construct using org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + ephemeralPublic_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + encryptedStatic_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + encryptedMessage_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000004); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.signal.libsignal.metadata.SignalProtos.internal_static_signal_UnidentifiedSenderMessage_descriptor; + } + + public org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage getDefaultInstanceForType() { + return org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.getDefaultInstance(); + } + + public org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage build() { + org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage buildPartial() { + org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage result = new org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.ephemeralPublic_ = ephemeralPublic_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.encryptedStatic_ = encryptedStatic_; + if (((from_bitField0_ & 0x00000004) == 0x00000004)) { + to_bitField0_ |= 0x00000004; + } + result.encryptedMessage_ = encryptedMessage_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage) { + return mergeFrom((org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage other) { + if (other == org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage.getDefaultInstance()) return this; + if (other.hasEphemeralPublic()) { + setEphemeralPublic(other.getEphemeralPublic()); + } + if (other.hasEncryptedStatic()) { + setEncryptedStatic(other.getEncryptedStatic()); + } + if (other.hasEncryptedMessage()) { + setEncryptedMessage(other.getEncryptedMessage()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.signal.libsignal.metadata.SignalProtos.UnidentifiedSenderMessage) e.getUnfinishedMessage(); + throw e; + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + // optional bytes ephemeralPublic = 1; + private com.google.protobuf.ByteString ephemeralPublic_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes ephemeralPublic = 1; + */ + public boolean hasEphemeralPublic() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + /** + * optional bytes ephemeralPublic = 1; + */ + public com.google.protobuf.ByteString getEphemeralPublic() { + return ephemeralPublic_; + } + /** + * optional bytes ephemeralPublic = 1; + */ + public Builder setEphemeralPublic(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + ephemeralPublic_ = value; + onChanged(); + return this; + } + /** + * optional bytes ephemeralPublic = 1; + */ + public Builder clearEphemeralPublic() { + bitField0_ = (bitField0_ & ~0x00000001); + ephemeralPublic_ = getDefaultInstance().getEphemeralPublic(); + onChanged(); + return this; + } + + // optional bytes encryptedStatic = 2; + private com.google.protobuf.ByteString encryptedStatic_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes encryptedStatic = 2; + */ + public boolean hasEncryptedStatic() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + /** + * optional bytes encryptedStatic = 2; + */ + public com.google.protobuf.ByteString getEncryptedStatic() { + return encryptedStatic_; + } + /** + * optional bytes encryptedStatic = 2; + */ + public Builder setEncryptedStatic(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + encryptedStatic_ = value; + onChanged(); + return this; + } + /** + * optional bytes encryptedStatic = 2; + */ + public Builder clearEncryptedStatic() { + bitField0_ = (bitField0_ & ~0x00000002); + encryptedStatic_ = getDefaultInstance().getEncryptedStatic(); + onChanged(); + return this; + } + + // optional bytes encryptedMessage = 3; + private com.google.protobuf.ByteString encryptedMessage_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes encryptedMessage = 3; + */ + public boolean hasEncryptedMessage() { + return ((bitField0_ & 0x00000004) == 0x00000004); + } + /** + * optional bytes encryptedMessage = 3; + */ + public com.google.protobuf.ByteString getEncryptedMessage() { + return encryptedMessage_; + } + /** + * optional bytes encryptedMessage = 3; + */ + public Builder setEncryptedMessage(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000004; + encryptedMessage_ = value; + onChanged(); + return this; + } + /** + * optional bytes encryptedMessage = 3; + */ + public Builder clearEncryptedMessage() { + bitField0_ = (bitField0_ & ~0x00000004); + encryptedMessage_ = getDefaultInstance().getEncryptedMessage(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:signal.UnidentifiedSenderMessage) + } + + static { + defaultInstance = new UnidentifiedSenderMessage(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:signal.UnidentifiedSenderMessage) + } + + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signal_ServerCertificate_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signal_ServerCertificate_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signal_ServerCertificate_Certificate_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signal_ServerCertificate_Certificate_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signal_SenderCertificate_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signal_SenderCertificate_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signal_SenderCertificate_Certificate_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signal_SenderCertificate_Certificate_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signal_UnidentifiedSenderMessage_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signal_UnidentifiedSenderMessage_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_signal_UnidentifiedSenderMessage_Message_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_signal_UnidentifiedSenderMessage_Message_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\032UnidentifiedDelivery.proto\022\006signal\"c\n\021" + + "ServerCertificate\022\023\n\013certificate\030\001 \001(\014\022\021" + + "\n\tsignature\030\002 \001(\014\032&\n\013Certificate\022\n\n\002id\030\001" + + " \001(\r\022\013\n\003key\030\002 \001(\014\"\302\001\n\021SenderCertificate\022" + + "\023\n\013certificate\030\001 \001(\014\022\021\n\tsignature\030\002 \001(\014\032" + + "\204\001\n\013Certificate\022\016\n\006sender\030\001 \001(\t\022\024\n\014sende" + + "rDevice\030\002 \001(\r\022\017\n\007expires\030\003 \001(\006\022\023\n\013identi" + + "tyKey\030\004 \001(\014\022)\n\006signer\030\005 \001(\0132\031.signal.Ser" + + "verCertificate\"\241\002\n\031UnidentifiedSenderMes" + + "sage\022\027\n\017ephemeralPublic\030\001 \001(\014\022\027\n\017encrypt", + "edStatic\030\002 \001(\014\022\030\n\020encryptedMessage\030\003 \001(\014" + + "\032\267\001\n\007Message\022<\n\004type\030\001 \001(\0162..signal.Unid" + + "entifiedSenderMessage.Message.Type\0224\n\021se" + + "nderCertificate\030\002 \001(\0132\031.signal.SenderCer" + + "tificate\022\017\n\007content\030\003 \001(\014\"\'\n\004Type\022\022\n\016PRE" + + "KEY_MESSAGE\020\001\022\013\n\007MESSAGE\020\002B-\n\035org.signal" + + ".libsignal.metadataB\014SignalProtos" + }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = + new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + internal_static_signal_ServerCertificate_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_signal_ServerCertificate_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signal_ServerCertificate_descriptor, + new java.lang.String[] { "Certificate", "Signature", }); + internal_static_signal_ServerCertificate_Certificate_descriptor = + internal_static_signal_ServerCertificate_descriptor.getNestedTypes().get(0); + internal_static_signal_ServerCertificate_Certificate_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signal_ServerCertificate_Certificate_descriptor, + new java.lang.String[] { "Id", "Key", }); + internal_static_signal_SenderCertificate_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_signal_SenderCertificate_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signal_SenderCertificate_descriptor, + new java.lang.String[] { "Certificate", "Signature", }); + internal_static_signal_SenderCertificate_Certificate_descriptor = + internal_static_signal_SenderCertificate_descriptor.getNestedTypes().get(0); + internal_static_signal_SenderCertificate_Certificate_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signal_SenderCertificate_Certificate_descriptor, + new java.lang.String[] { "Sender", "SenderDevice", "Expires", "IdentityKey", "Signer", }); + internal_static_signal_UnidentifiedSenderMessage_descriptor = + getDescriptor().getMessageTypes().get(2); + internal_static_signal_UnidentifiedSenderMessage_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signal_UnidentifiedSenderMessage_descriptor, + new java.lang.String[] { "EphemeralPublic", "EncryptedStatic", "EncryptedMessage", }); + internal_static_signal_UnidentifiedSenderMessage_Message_descriptor = + internal_static_signal_UnidentifiedSenderMessage_descriptor.getNestedTypes().get(0); + internal_static_signal_UnidentifiedSenderMessage_Message_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_signal_UnidentifiedSenderMessage_Message_descriptor, + new java.lang.String[] { "Type", "SenderCertificate", "Content", }); + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }, assigner); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/java/src/main/java/org/signal/libsignal/metadata/certificate/CertificateValidator.java b/java/src/main/java/org/signal/libsignal/metadata/certificate/CertificateValidator.java new file mode 100644 index 0000000..030490d --- /dev/null +++ b/java/src/main/java/org/signal/libsignal/metadata/certificate/CertificateValidator.java @@ -0,0 +1,56 @@ +package org.signal.libsignal.metadata.certificate; + + +import org.whispersystems.libsignal.InvalidKeyException; +import org.whispersystems.libsignal.ecc.Curve; +import org.whispersystems.libsignal.ecc.ECPublicKey; + +import java.util.HashSet; +import java.util.Set; + +public class CertificateValidator { + + @SuppressWarnings("MismatchedQueryAndUpdateOfCollection") + private static final Set REVOKED = new HashSet() {{ + + }}; + + private final ECPublicKey trustRoot; + + public CertificateValidator(ECPublicKey trustRoot) { + this.trustRoot = trustRoot; + } + + public void validate(SenderCertificate certificate, long validationTime) throws InvalidCertificateException { + try { + ServerCertificate serverCertificate = certificate.getSigner(); + validate(serverCertificate); + + if (!Curve.verifySignature(serverCertificate.getKey(), certificate.getCertificate(), certificate.getSignature())) { + throw new InvalidCertificateException("Signature failed"); + } + + if (validationTime > certificate.getExpiration()) { + throw new InvalidCertificateException("Certificate is expired"); + } + } catch (InvalidKeyException e) { + throw new InvalidCertificateException(e); + } + } + + // VisibleForTesting + void validate(ServerCertificate certificate) throws InvalidCertificateException { + try { + if (!Curve.verifySignature(trustRoot, certificate.getCertificate(), certificate.getSignature())) { + throw new InvalidCertificateException("Signature failed"); + } + + if (REVOKED.contains(certificate.getKeyId())) { + throw new InvalidCertificateException("Server certificate has been revoked"); + } + } catch (InvalidKeyException e) { + throw new InvalidCertificateException(e); + } + } +} + diff --git a/java/src/main/java/org/signal/libsignal/metadata/certificate/InvalidCertificateException.java b/java/src/main/java/org/signal/libsignal/metadata/certificate/InvalidCertificateException.java new file mode 100644 index 0000000..f2ec146 --- /dev/null +++ b/java/src/main/java/org/signal/libsignal/metadata/certificate/InvalidCertificateException.java @@ -0,0 +1,12 @@ +package org.signal.libsignal.metadata.certificate; + + +public class InvalidCertificateException extends Exception { + public InvalidCertificateException(String s) { + super(s); + } + + public InvalidCertificateException(Exception e) { + super(e); + } +} diff --git a/java/src/main/java/org/signal/libsignal/metadata/certificate/SenderCertificate.java b/java/src/main/java/org/signal/libsignal/metadata/certificate/SenderCertificate.java new file mode 100644 index 0000000..54cba19 --- /dev/null +++ b/java/src/main/java/org/signal/libsignal/metadata/certificate/SenderCertificate.java @@ -0,0 +1,84 @@ +package org.signal.libsignal.metadata.certificate; + + +import com.google.protobuf.InvalidProtocolBufferException; + +import org.signal.libsignal.metadata.SignalProtos; +import org.whispersystems.libsignal.InvalidKeyException; +import org.whispersystems.libsignal.ecc.Curve; +import org.whispersystems.libsignal.ecc.ECPublicKey; + + +public class SenderCertificate { + + private final ServerCertificate signer; + private final ECPublicKey key; + private final int senderDeviceId; + private final String sender; + private final long expiration; + + private final byte[] serialized; + private final byte[] certificate; + private final byte[] signature; + + public SenderCertificate(byte[] serialized) throws InvalidCertificateException { + try { + SignalProtos.SenderCertificate wrapper = SignalProtos.SenderCertificate.parseFrom(serialized); + + if (!wrapper.hasSignature() || !wrapper.hasCertificate()) { + throw new InvalidCertificateException("Missing fields"); + } + + SignalProtos.SenderCertificate.Certificate certificate = SignalProtos.SenderCertificate.Certificate.parseFrom(wrapper.getCertificate()); + + if (!certificate.hasSigner() || !certificate.hasIdentityKey() || !certificate.hasSenderDevice() || !certificate.hasExpires() || !certificate.hasSender()) { + throw new InvalidCertificateException("Missing fields"); + } + + this.signer = new ServerCertificate(certificate.getSigner().toByteArray()); + this.key = Curve.decodePoint(certificate.getIdentityKey().toByteArray(), 0); + this.sender = certificate.getSender(); + this.senderDeviceId = certificate.getSenderDevice(); + this.expiration = certificate.getExpires(); + + this.serialized = serialized; + this.certificate = wrapper.getCertificate().toByteArray(); + this.signature = wrapper.getSignature().toByteArray(); + + } catch (InvalidProtocolBufferException | InvalidKeyException e) { + throw new InvalidCertificateException(e); + } + } + + public ServerCertificate getSigner() { + return signer; + } + + public ECPublicKey getKey() { + return key; + } + + public int getSenderDeviceId() { + return senderDeviceId; + } + + public String getSender() { + return sender; + } + + public long getExpiration() { + return expiration; + } + + public byte[] getSerialized() { + return serialized; + } + + public byte[] getCertificate() { + return certificate; + } + + public byte[] getSignature() { + return signature; + } +} diff --git a/java/src/main/java/org/signal/libsignal/metadata/certificate/ServerCertificate.java b/java/src/main/java/org/signal/libsignal/metadata/certificate/ServerCertificate.java new file mode 100644 index 0000000..8a3a158 --- /dev/null +++ b/java/src/main/java/org/signal/libsignal/metadata/certificate/ServerCertificate.java @@ -0,0 +1,64 @@ +package org.signal.libsignal.metadata.certificate; + + +import com.google.protobuf.InvalidProtocolBufferException; + +import org.signal.libsignal.metadata.SignalProtos; +import org.whispersystems.libsignal.InvalidKeyException; +import org.whispersystems.libsignal.ecc.Curve; +import org.whispersystems.libsignal.ecc.ECPublicKey; + +public class ServerCertificate { + + private final int keyId; + private final ECPublicKey key; + + private final byte[] serialized; + private final byte[] certificate; + private final byte[] signature; + + public ServerCertificate(byte[] serialized) throws InvalidCertificateException { + try { + SignalProtos.ServerCertificate wrapper = SignalProtos.ServerCertificate.parseFrom(serialized); + + if (!wrapper.hasCertificate() || !wrapper.hasSignature()) { + throw new InvalidCertificateException("Missing fields"); + } + + SignalProtos.ServerCertificate.Certificate certificate = SignalProtos.ServerCertificate.Certificate.parseFrom(wrapper.getCertificate()); + + if (!certificate.hasId() || !certificate.hasKey()) { + throw new InvalidCertificateException("Missing fields"); + } + + this.keyId = certificate.getId(); + this.key = Curve.decodePoint(certificate.getKey().toByteArray(), 0); + this.serialized = serialized; + this.certificate = wrapper.getCertificate().toByteArray(); + this.signature = wrapper.getSignature().toByteArray(); + + } catch (InvalidProtocolBufferException | InvalidKeyException e) { + throw new InvalidCertificateException(e); + } + } + + public int getKeyId() { + return keyId; + } + + public ECPublicKey getKey() { + return key; + } + + public byte[] getSerialized() { + return serialized; + } + + public byte[] getCertificate() { + return certificate; + } + + public byte[] getSignature() { + return signature; + } +} diff --git a/java/src/main/java/org/signal/libsignal/metadata/protocol/UnidentifiedSenderMessage.java b/java/src/main/java/org/signal/libsignal/metadata/protocol/UnidentifiedSenderMessage.java new file mode 100644 index 0000000..2bb78bf --- /dev/null +++ b/java/src/main/java/org/signal/libsignal/metadata/protocol/UnidentifiedSenderMessage.java @@ -0,0 +1,88 @@ +package org.signal.libsignal.metadata.protocol; + + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.omg.CORBA.DynAnyPackage.Invalid; +import org.signal.libsignal.metadata.InvalidMetadataMessageException; +import org.signal.libsignal.metadata.InvalidMetadataVersionException; +import org.signal.libsignal.metadata.SignalProtos; +import org.whispersystems.libsignal.InvalidKeyException; +import org.whispersystems.libsignal.InvalidMessageException; +import org.whispersystems.libsignal.InvalidVersionException; +import org.whispersystems.libsignal.ecc.Curve; +import org.whispersystems.libsignal.ecc.ECPublicKey; +import org.whispersystems.libsignal.util.ByteUtil; + +public class UnidentifiedSenderMessage { + + private static final int CIPHERTEXT_VERSION = 1; + + private final int version; + private final ECPublicKey ephemeral; + private final byte[] encryptedStatic; + private final byte[] encryptedMessage; + private final byte[] serialized; + + public UnidentifiedSenderMessage(byte[] serialized) + throws InvalidMetadataMessageException, InvalidMetadataVersionException + { + try { + this.version = ByteUtil.highBitsToInt(serialized[0]); + + if (version > CIPHERTEXT_VERSION) { + throw new InvalidMetadataVersionException("Unknown version: " + this.version); + } + + SignalProtos.UnidentifiedSenderMessage unidentifiedSenderMessage = SignalProtos.UnidentifiedSenderMessage.parseFrom(ByteString.copyFrom(serialized, 1, serialized.length - 1)); + + if (!unidentifiedSenderMessage.hasEphemeralPublic() || + !unidentifiedSenderMessage.hasEncryptedStatic() || + !unidentifiedSenderMessage.hasEncryptedMessage()) + { + throw new InvalidMetadataMessageException("Missing fields"); + } + + this.ephemeral = Curve.decodePoint(unidentifiedSenderMessage.getEphemeralPublic().toByteArray(), 0); + this.encryptedStatic = unidentifiedSenderMessage.getEncryptedStatic().toByteArray(); + this.encryptedMessage = unidentifiedSenderMessage.getEncryptedMessage().toByteArray(); + this.serialized = serialized; + } catch (InvalidProtocolBufferException | InvalidKeyException e) { + throw new InvalidMetadataMessageException(e); + } + } + + public UnidentifiedSenderMessage(ECPublicKey ephemeral, byte[] encryptedStatic, byte[] encryptedMessage) { + this.version = CIPHERTEXT_VERSION; + this.ephemeral = ephemeral; + this.encryptedStatic = encryptedStatic; + this.encryptedMessage = encryptedMessage; + + byte[] versionBytes = {ByteUtil.intsToByteHighAndLow(CIPHERTEXT_VERSION, CIPHERTEXT_VERSION)}; + byte[] messageBytes = SignalProtos.UnidentifiedSenderMessage.newBuilder() + .setEncryptedMessage(ByteString.copyFrom(encryptedMessage)) + .setEncryptedStatic(ByteString.copyFrom(encryptedStatic)) + .setEphemeralPublic(ByteString.copyFrom(ephemeral.serialize())) + .build() + .toByteArray(); + + this.serialized = ByteUtil.combine(versionBytes, messageBytes); + } + + public ECPublicKey getEphemeral() { + return ephemeral; + } + + public byte[] getEncryptedStatic() { + return encryptedStatic; + } + + public byte[] getEncryptedMessage() { + return encryptedMessage; + } + + public byte[] getSerialized() { + return serialized; + } +} diff --git a/java/src/main/java/org/signal/libsignal/metadata/protocol/UnidentifiedSenderMessageContent.java b/java/src/main/java/org/signal/libsignal/metadata/protocol/UnidentifiedSenderMessageContent.java new file mode 100644 index 0000000..24656f4 --- /dev/null +++ b/java/src/main/java/org/signal/libsignal/metadata/protocol/UnidentifiedSenderMessageContent.java @@ -0,0 +1,84 @@ +package org.signal.libsignal.metadata.protocol; + + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.signal.libsignal.metadata.InvalidMetadataMessageException; +import org.signal.libsignal.metadata.SignalProtos; +import org.signal.libsignal.metadata.certificate.InvalidCertificateException; +import org.signal.libsignal.metadata.certificate.SenderCertificate; +import org.whispersystems.libsignal.InvalidMessageException; +import org.whispersystems.libsignal.protocol.CiphertextMessage; + +public class UnidentifiedSenderMessageContent { + + private final int type; + private final SenderCertificate senderCertificate; + private final byte[] content; + private final byte[] serialized; + + public UnidentifiedSenderMessageContent(byte[] serialized) throws InvalidMetadataMessageException, InvalidCertificateException { + try { + SignalProtos.UnidentifiedSenderMessage.Message message = SignalProtos.UnidentifiedSenderMessage.Message.parseFrom(serialized); + + if (!message.hasType() || !message.hasSenderCertificate() || !message.hasContent()) { + throw new InvalidMetadataMessageException("Missing fields"); + } + + switch (message.getType()) { + case MESSAGE: this.type = CiphertextMessage.WHISPER_TYPE; break; + case PREKEY_MESSAGE: this.type = CiphertextMessage.PREKEY_TYPE; break; + default: throw new InvalidMetadataMessageException("Unknown type: " + message.getType().getNumber()); + } + + this.senderCertificate = new SenderCertificate(message.getSenderCertificate().toByteArray()); + this.content = message.getContent().toByteArray(); + this.serialized = serialized; + } catch (InvalidProtocolBufferException e) { + throw new InvalidMetadataMessageException(e); + } + } + + public UnidentifiedSenderMessageContent(int type, SenderCertificate senderCertificate, byte[] content) { + try { + this.serialized = SignalProtos.UnidentifiedSenderMessage.Message.newBuilder() + .setType(SignalProtos.UnidentifiedSenderMessage.Message.Type.valueOf(getProtoType(type))) + .setSenderCertificate(SignalProtos.SenderCertificate.parseFrom(senderCertificate.getSerialized())) + .setContent(ByteString.copyFrom(content)) + .build() + .toByteArray(); + + this.type = type; + this.senderCertificate = senderCertificate; + this.content = content; + } catch (InvalidProtocolBufferException e) { + throw new AssertionError(e); + } + } + + public int getType() { + return type; + } + + public SenderCertificate getSenderCertificate() { + return senderCertificate; + } + + public byte[] getContent() { + return content; + } + + public byte[] getSerialized() { + return serialized; + } + + private int getProtoType(int type) { + switch (type) { + case CiphertextMessage.WHISPER_TYPE: return SignalProtos.UnidentifiedSenderMessage.Message.Type.MESSAGE_VALUE; + case CiphertextMessage.PREKEY_TYPE: return SignalProtos.UnidentifiedSenderMessage.Message.Type.PREKEY_MESSAGE_VALUE; + default: throw new AssertionError(type); + } + } + +} diff --git a/protobuf/Makefile b/protobuf/Makefile new file mode 100644 index 0000000..3551ade --- /dev/null +++ b/protobuf/Makefile @@ -0,0 +1,3 @@ + +all: + protoc --java_out=../java/src/main/java/ UnidentifiedDelivery.proto diff --git a/protobuf/UnidentifiedDelivery.proto b/protobuf/UnidentifiedDelivery.proto new file mode 100644 index 0000000..6aad325 --- /dev/null +++ b/protobuf/UnidentifiedDelivery.proto @@ -0,0 +1,45 @@ +package signal; + +option java_package = "org.signal.libsignal.metadata"; +option java_outer_classname = "SignalProtos"; + +message ServerCertificate { + message Certificate { + optional uint32 id = 1; + optional bytes key = 2; + } + + optional bytes certificate = 1; + optional bytes signature = 2; +} + +message SenderCertificate { + message Certificate { + optional string sender = 1; + optional uint32 senderDevice = 2; + optional fixed64 expires = 3; + optional bytes identityKey = 4; + optional ServerCertificate signer = 5; + } + + optional bytes certificate = 1; + optional bytes signature = 2; +} + +message UnidentifiedSenderMessage { + + message Message { + enum Type { + PREKEY_MESSAGE = 1; + MESSAGE = 2; + } + + optional Type type = 1; + optional SenderCertificate senderCertificate = 2; + optional bytes content = 3; + } + + optional bytes ephemeralPublic = 1; + optional bytes encryptedStatic = 2; + optional bytes encryptedMessage = 3; +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 0000000..7412250 --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +include ':java', ':android', ':tests' diff --git a/tests/build.gradle b/tests/build.gradle new file mode 100644 index 0000000..013779e --- /dev/null +++ b/tests/build.gradle @@ -0,0 +1,12 @@ +apply plugin: 'java' + +repositories { + mavenCentral() + mavenLocal() +} + +dependencies { + testCompile 'junit:junit:3.8.2' + + compile project(':java') +} \ No newline at end of file diff --git a/tests/src/test/java/org/signal/libsignal/metadata/SealedSessionCipherTest.java b/tests/src/test/java/org/signal/libsignal/metadata/SealedSessionCipherTest.java new file mode 100644 index 0000000..f64b079 --- /dev/null +++ b/tests/src/test/java/org/signal/libsignal/metadata/SealedSessionCipherTest.java @@ -0,0 +1,175 @@ +package org.signal.libsignal.metadata; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import junit.framework.TestCase; + +import org.signal.libsignal.metadata.certificate.CertificateValidator; +import org.signal.libsignal.metadata.certificate.InvalidCertificateException; +import org.signal.libsignal.metadata.certificate.SenderCertificate; +import org.signal.libsignal.metadata.certificate.ServerCertificate; +import org.whispersystems.libsignal.IdentityKeyPair; +import org.whispersystems.libsignal.InvalidKeyException; +import org.whispersystems.libsignal.SessionBuilder; +import org.whispersystems.libsignal.SignalProtocolAddress; +import org.whispersystems.libsignal.UntrustedIdentityException; +import org.whispersystems.libsignal.ecc.Curve; +import org.whispersystems.libsignal.ecc.ECKeyPair; +import org.whispersystems.libsignal.ecc.ECPublicKey; +import org.whispersystems.libsignal.state.PreKeyBundle; +import org.whispersystems.libsignal.state.PreKeyRecord; +import org.whispersystems.libsignal.state.SignedPreKeyRecord; +import org.whispersystems.libsignal.util.KeyHelper; +import org.whispersystems.libsignal.util.Pair; + +public class SealedSessionCipherTest extends TestCase { + + public void testEncryptDecrypt() throws UntrustedIdentityException, InvalidKeyException, InvalidCertificateException, InvalidProtocolBufferException, InvalidMetadataMessageException, ProtocolDuplicateMessageException, ProtocolUntrustedIdentityException, ProtocolLegacyMessageException, ProtocolInvalidKeyException, InvalidMetadataVersionException, ProtocolInvalidVersionException, ProtocolInvalidMessageException, ProtocolInvalidKeyIdException, ProtocolNoSessionException, SelfSendException { + TestInMemorySignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); + TestInMemorySignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); + + initializeSessions(aliceStore, bobStore); + + ECKeyPair trustRoot = Curve.generateKeyPair(); + SenderCertificate senderCertificate = createCertificateFor(trustRoot, "+14151111111", 1, aliceStore.getIdentityKeyPair().getPublicKey().getPublicKey(), 31337); + SealedSessionCipher aliceCipher = new SealedSessionCipher(aliceStore, new SignalProtocolAddress("+14151111111", 1)); + + byte[] ciphertext = aliceCipher.encrypt(new SignalProtocolAddress("+14152222222", 1), + senderCertificate, "smert za smert".getBytes()); + + + SealedSessionCipher bobCipher = new SealedSessionCipher(bobStore, new SignalProtocolAddress("+14152222222", 1)); + + Pair plaintext = bobCipher.decrypt(new CertificateValidator(trustRoot.getPublicKey()), ciphertext, 31335); + + assertEquals(new String(plaintext.second()), "smert za smert"); + assertEquals(plaintext.first().getName(), "+14151111111"); + assertEquals(plaintext.first().getDeviceId(), 1); + } + + public void testEncryptDecryptUntrusted() throws Exception { + TestInMemorySignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); + TestInMemorySignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); + + initializeSessions(aliceStore, bobStore); + + ECKeyPair trustRoot = Curve.generateKeyPair(); + ECKeyPair falseTrustRoot = Curve.generateKeyPair(); + SenderCertificate senderCertificate = createCertificateFor(falseTrustRoot, "+14151111111", 1, aliceStore.getIdentityKeyPair().getPublicKey().getPublicKey(), 31337); + SealedSessionCipher aliceCipher = new SealedSessionCipher(aliceStore, new SignalProtocolAddress("+14151111111", 1)); + + byte[] ciphertext = aliceCipher.encrypt(new SignalProtocolAddress("+14152222222", 1), + senderCertificate, "и вот я".getBytes()); + + SealedSessionCipher bobCipher = new SealedSessionCipher(bobStore, new SignalProtocolAddress("+14152222222",1)); + + try { + bobCipher.decrypt(new CertificateValidator(trustRoot.getPublicKey()), ciphertext, 31335); + throw new AssertionError(); + } catch (InvalidMetadataMessageException e) { + // good + } + } + + public void testEncryptDecryptExpired() throws Exception { + TestInMemorySignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); + TestInMemorySignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); + + initializeSessions(aliceStore, bobStore); + + ECKeyPair trustRoot = Curve.generateKeyPair(); + SenderCertificate senderCertificate = createCertificateFor(trustRoot, "+14151111111", 1, aliceStore.getIdentityKeyPair().getPublicKey().getPublicKey(), 31337); + SealedSessionCipher aliceCipher = new SealedSessionCipher(aliceStore, new SignalProtocolAddress("+14151111111", 1)); + + byte[] ciphertext = aliceCipher.encrypt(new SignalProtocolAddress("+14152222222", 1), + senderCertificate, "и вот я".getBytes()); + + SealedSessionCipher bobCipher = new SealedSessionCipher(bobStore, new SignalProtocolAddress("+14152222222", 1)); + + try { + bobCipher.decrypt(new CertificateValidator(trustRoot.getPublicKey()), ciphertext, 31338); + throw new AssertionError(); + } catch (InvalidMetadataMessageException e) { + // good + } + } + + public void testEncryptFromWrongIdentity() throws Exception { + TestInMemorySignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); + TestInMemorySignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); + + initializeSessions(aliceStore, bobStore); + + ECKeyPair trustRoot = Curve.generateKeyPair(); + ECKeyPair randomKeyPair = Curve.generateKeyPair(); + SenderCertificate senderCertificate = createCertificateFor(trustRoot, "+14151111111", 1, randomKeyPair.getPublicKey(), 31337); + SealedSessionCipher aliceCipher = new SealedSessionCipher(aliceStore, new SignalProtocolAddress("+14151111111", 1)); + + byte[] ciphertext = aliceCipher.encrypt(new SignalProtocolAddress("+14152222222", 1), + senderCertificate, "smert za smert".getBytes()); + + + SealedSessionCipher bobCipher = new SealedSessionCipher(bobStore, new SignalProtocolAddress("+14152222222", 1)); + + try { + bobCipher.decrypt(new CertificateValidator(trustRoot.getPublicKey()), ciphertext, 31335); + } catch (InvalidMetadataMessageException e) { + // good + } + } + + + + private SenderCertificate createCertificateFor(ECKeyPair trustRoot, String sender, int deviceId, ECPublicKey identityKey, long expires) + throws InvalidKeyException, InvalidCertificateException, InvalidProtocolBufferException { + ECKeyPair serverKey = Curve.generateKeyPair(); + + byte[] serverCertificateBytes = SignalProtos.ServerCertificate.Certificate.newBuilder() + .setId(1) + .setKey(ByteString.copyFrom(serverKey.getPublicKey().serialize())) + .build() + .toByteArray(); + + byte[] serverCertificateSignature = Curve.calculateSignature(trustRoot.getPrivateKey(), serverCertificateBytes); + + ServerCertificate serverCertificate = new ServerCertificate(SignalProtos.ServerCertificate.newBuilder() + .setCertificate(ByteString.copyFrom(serverCertificateBytes)) + .setSignature(ByteString.copyFrom(serverCertificateSignature)) + .build() + .toByteArray()); + + byte[] senderCertificateBytes = SignalProtos.SenderCertificate.Certificate.newBuilder() + .setSender(sender) + .setSenderDevice(deviceId) + .setIdentityKey(ByteString.copyFrom(identityKey.serialize())) + .setExpires(expires) + .setSigner(SignalProtos.ServerCertificate.parseFrom(serverCertificate.getSerialized())) + .build() + .toByteArray(); + + byte[] senderCertificateSignature = Curve.calculateSignature(serverKey.getPrivateKey(), senderCertificateBytes); + + return new SenderCertificate(SignalProtos.SenderCertificate.newBuilder() + .setCertificate(ByteString.copyFrom(senderCertificateBytes)) + .setSignature(ByteString.copyFrom(senderCertificateSignature)) + .build() + .toByteArray()); + } + + private void initializeSessions(TestInMemorySignalProtocolStore aliceStore, TestInMemorySignalProtocolStore bobStore) + throws InvalidKeyException, UntrustedIdentityException + { + ECKeyPair bobPreKey = Curve.generateKeyPair(); + IdentityKeyPair bobIdentityKey = bobStore.getIdentityKeyPair(); + SignedPreKeyRecord bobSignedPreKey = KeyHelper.generateSignedPreKey(bobIdentityKey, 2); + + PreKeyBundle bobBundle = new PreKeyBundle(1, 1, 1, bobPreKey.getPublicKey(), 2, bobSignedPreKey.getKeyPair().getPublicKey(), bobSignedPreKey.getSignature(), bobIdentityKey.getPublicKey()); + SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceStore, new SignalProtocolAddress("+14152222222", 1)); + aliceSessionBuilder.process(bobBundle); + + bobStore.storeSignedPreKey(2, bobSignedPreKey); + bobStore.storePreKey(1, new PreKeyRecord(1, bobPreKey)); + + } +} diff --git a/tests/src/test/java/org/signal/libsignal/metadata/SessionCipherTest.java b/tests/src/test/java/org/signal/libsignal/metadata/SessionCipherTest.java new file mode 100644 index 0000000..f54d089 --- /dev/null +++ b/tests/src/test/java/org/signal/libsignal/metadata/SessionCipherTest.java @@ -0,0 +1,193 @@ +package org.signal.libsignal.metadata; + + +import junit.framework.TestCase; + +import org.whispersystems.libsignal.DuplicateMessageException; +import org.whispersystems.libsignal.IdentityKey; +import org.whispersystems.libsignal.IdentityKeyPair; +import org.whispersystems.libsignal.InvalidKeyException; +import org.whispersystems.libsignal.InvalidMessageException; +import org.whispersystems.libsignal.LegacyMessageException; +import org.whispersystems.libsignal.NoSessionException; +import org.whispersystems.libsignal.SessionCipher; +import org.whispersystems.libsignal.SignalProtocolAddress; +import org.whispersystems.libsignal.UntrustedIdentityException; +import org.whispersystems.libsignal.ecc.Curve; +import org.whispersystems.libsignal.ecc.ECKeyPair; +import org.whispersystems.libsignal.ecc.ECPublicKey; +import org.whispersystems.libsignal.protocol.CiphertextMessage; +import org.whispersystems.libsignal.protocol.SignalMessage; +import org.whispersystems.libsignal.ratchet.AliceSignalProtocolParameters; +import org.whispersystems.libsignal.ratchet.BobSignalProtocolParameters; +import org.whispersystems.libsignal.ratchet.RatchetingSession; +import org.whispersystems.libsignal.state.SessionRecord; +import org.whispersystems.libsignal.state.SessionState; +import org.whispersystems.libsignal.state.SignalProtocolStore; +import org.whispersystems.libsignal.util.guava.Optional; + +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; + +public class SessionCipherTest extends TestCase { + + public void testBasicSessionV3() + throws InvalidKeyException, DuplicateMessageException, + LegacyMessageException, InvalidMessageException, NoSuchAlgorithmException, NoSessionException, UntrustedIdentityException + { + SessionRecord aliceSessionRecord = new SessionRecord(); + SessionRecord bobSessionRecord = new SessionRecord(); + + initializeSessionsV3(aliceSessionRecord.getSessionState(), bobSessionRecord.getSessionState()); + runInteraction(aliceSessionRecord, bobSessionRecord); + } + + public void testMessageKeyLimits() throws Exception { + SessionRecord aliceSessionRecord = new SessionRecord(); + SessionRecord bobSessionRecord = new SessionRecord(); + + initializeSessionsV3(aliceSessionRecord.getSessionState(), bobSessionRecord.getSessionState()); + + SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); + SignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); + + aliceStore.storeSession(new SignalProtocolAddress("+14159999999", 1), aliceSessionRecord); + bobStore.storeSession(new SignalProtocolAddress("+14158888888", 1), bobSessionRecord); + + SessionCipher aliceCipher = new SessionCipher(aliceStore, new SignalProtocolAddress("+14159999999", 1)); + SessionCipher bobCipher = new SessionCipher(bobStore, new SignalProtocolAddress("+14158888888", 1)); + + List inflight = new LinkedList<>(); + + for (int i=0;i<2010;i++) { + inflight.add(aliceCipher.encrypt("you've never been so hungry, you've never been so cold".getBytes())); + } + + bobCipher.decrypt(new SignalMessage(inflight.get(1000).serialize())); + bobCipher.decrypt(new SignalMessage(inflight.get(inflight.size()-1).serialize())); + + try { + bobCipher.decrypt(new SignalMessage(inflight.get(0).serialize())); + throw new AssertionError("Should have failed!"); + } catch (DuplicateMessageException dme) { + // good + } + } + + private void runInteraction(SessionRecord aliceSessionRecord, SessionRecord bobSessionRecord) + throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, NoSuchAlgorithmException, NoSessionException, UntrustedIdentityException { + SignalProtocolStore aliceStore = new TestInMemorySignalProtocolStore(); + SignalProtocolStore bobStore = new TestInMemorySignalProtocolStore(); + + aliceStore.storeSession(new SignalProtocolAddress("+14159999999", 1), aliceSessionRecord); + bobStore.storeSession(new SignalProtocolAddress("+14158888888", 1), bobSessionRecord); + + SessionCipher aliceCipher = new SessionCipher(aliceStore, new SignalProtocolAddress("+14159999999", 1)); + SessionCipher bobCipher = new SessionCipher(bobStore, new SignalProtocolAddress("+14158888888", 1)); + + byte[] alicePlaintext = "This is a plaintext message.".getBytes(); + CiphertextMessage message = aliceCipher.encrypt(alicePlaintext); + byte[] bobPlaintext = bobCipher.decrypt(new SignalMessage(message.serialize())); + + assertTrue(Arrays.equals(alicePlaintext, bobPlaintext)); + + byte[] bobReply = "This is a message from Bob.".getBytes(); + CiphertextMessage reply = bobCipher.encrypt(bobReply); + byte[] receivedReply = aliceCipher.decrypt(new SignalMessage(reply.serialize())); + + assertTrue(Arrays.equals(bobReply, receivedReply)); + + List aliceCiphertextMessages = new ArrayList<>(); + List alicePlaintextMessages = new ArrayList<>(); + + for (int i=0;i<50;i++) { + alicePlaintextMessages.add(("смерть за смерть " + i).getBytes()); + aliceCiphertextMessages.add(aliceCipher.encrypt(("смерть за смерть " + i).getBytes())); + } + + long seed = System.currentTimeMillis(); + + Collections.shuffle(aliceCiphertextMessages, new Random(seed)); + Collections.shuffle(alicePlaintextMessages, new Random(seed)); + + for (int i=0;i bobCiphertextMessages = new ArrayList<>(); + List bobPlaintextMessages = new ArrayList<>(); + + for (int i=0;i<20;i++) { + bobPlaintextMessages.add(("смерть за смерть " + i).getBytes()); + bobCiphertextMessages.add(bobCipher.encrypt(("смерть за смерть " + i).getBytes())); + } + + seed = System.currentTimeMillis(); + + Collections.shuffle(bobCiphertextMessages, new Random(seed)); + Collections.shuffle(bobPlaintextMessages, new Random(seed)); + + for (int i=0;iabsent()) + .setTheirRatchetKey(bobEphemeralKey.getPublicKey()) + .setTheirSignedPreKey(bobBaseKey.getPublicKey()) + .setTheirIdentityKey(bobIdentityKey.getPublicKey()) + .create(); + + BobSignalProtocolParameters bobParameters = BobSignalProtocolParameters.newBuilder() + .setOurRatchetKey(bobEphemeralKey) + .setOurSignedPreKey(bobBaseKey) + .setOurOneTimePreKey(Optional.absent()) + .setOurIdentityKey(bobIdentityKey) + .setTheirIdentityKey(aliceIdentityKey.getPublicKey()) + .setTheirBaseKey(aliceBaseKey.getPublicKey()) + .create(); + + RatchetingSession.initializeSession(aliceSessionState, aliceParameters); + RatchetingSession.initializeSession(bobSessionState, bobParameters); + } + +} diff --git a/tests/src/test/java/org/signal/libsignal/metadata/TestInMemorySignalProtocolStore.java b/tests/src/test/java/org/signal/libsignal/metadata/TestInMemorySignalProtocolStore.java new file mode 100644 index 0000000..25ac41e --- /dev/null +++ b/tests/src/test/java/org/signal/libsignal/metadata/TestInMemorySignalProtocolStore.java @@ -0,0 +1,27 @@ +package org.signal.libsignal.metadata; + + +import org.whispersystems.libsignal.IdentityKey; +import org.whispersystems.libsignal.IdentityKeyPair; +import org.whispersystems.libsignal.ecc.Curve; +import org.whispersystems.libsignal.ecc.ECKeyPair; +import org.whispersystems.libsignal.state.SignedPreKeyRecord; +import org.whispersystems.libsignal.state.impl.InMemorySignalProtocolStore; +import org.whispersystems.libsignal.util.KeyHelper; + +public class TestInMemorySignalProtocolStore extends InMemorySignalProtocolStore { + public TestInMemorySignalProtocolStore() { + super(generateIdentityKeyPair(), generateRegistrationId()); + } + + private static IdentityKeyPair generateIdentityKeyPair() { + ECKeyPair identityKeyPairKeys = Curve.generateKeyPair(); + + return new IdentityKeyPair(new IdentityKey(identityKeyPairKeys.getPublicKey()), + identityKeyPairKeys.getPrivateKey()); + } + + private static int generateRegistrationId() { + return KeyHelper.generateRegistrationId(false); + } +} \ No newline at end of file diff --git a/tests/src/test/java/org/signal/libsignal/metadata/certificate/SenderCertificateTest.java b/tests/src/test/java/org/signal/libsignal/metadata/certificate/SenderCertificateTest.java new file mode 100644 index 0000000..1b710d3 --- /dev/null +++ b/tests/src/test/java/org/signal/libsignal/metadata/certificate/SenderCertificateTest.java @@ -0,0 +1,127 @@ +package org.signal.libsignal.metadata.certificate; + + +import com.google.protobuf.ByteString; + +import junit.framework.TestCase; + +import org.signal.libsignal.metadata.SignalProtos; +import org.whispersystems.libsignal.InvalidKeyException; +import org.whispersystems.libsignal.ecc.Curve; +import org.whispersystems.libsignal.ecc.ECKeyPair; + +public class SenderCertificateTest extends TestCase { + + private final ECKeyPair trustRoot = Curve.generateKeyPair(); + + public void testSignature() throws InvalidCertificateException, InvalidKeyException { + ECKeyPair serverKey = Curve.generateKeyPair(); + ECKeyPair key = Curve.generateKeyPair(); + + byte[] certificateBytes = SignalProtos.SenderCertificate.Certificate.newBuilder() + .setSender("+14152222222") + .setSenderDevice(1) + .setExpires(31337) + .setIdentityKey(ByteString.copyFrom(key.getPublicKey().serialize())) + .setSigner(getServerCertificate(serverKey)) + .build() + .toByteArray(); + + byte[] certificateSignature = Curve.calculateSignature(serverKey.getPrivateKey(), certificateBytes); + + SenderCertificate senderCertificate = new SenderCertificate(SignalProtos.SenderCertificate.newBuilder() + .setCertificate(ByteString.copyFrom(certificateBytes)) + .setSignature(ByteString.copyFrom(certificateSignature)) + .build() + .toByteArray()); + + new CertificateValidator(trustRoot.getPublicKey()).validate(senderCertificate, 31336); + } + + public void testExpiredSignature() throws InvalidCertificateException, InvalidKeyException { + ECKeyPair serverKey = Curve.generateKeyPair(); + ECKeyPair key = Curve.generateKeyPair(); + + byte[] certificateBytes = SignalProtos.SenderCertificate.Certificate.newBuilder() + .setSender("+14152222222") + .setSenderDevice(1) + .setExpires(31337) + .setIdentityKey(ByteString.copyFrom(key.getPublicKey().serialize())) + .setSigner(getServerCertificate(serverKey)) + .build() + .toByteArray(); + + byte[] certificateSignature = Curve.calculateSignature(serverKey.getPrivateKey(), certificateBytes); + + SenderCertificate senderCertificate = new SenderCertificate(SignalProtos.SenderCertificate.newBuilder() + .setCertificate(ByteString.copyFrom(certificateBytes)) + .setSignature(ByteString.copyFrom(certificateSignature)) + .build() + .toByteArray()); + + try { + new CertificateValidator(trustRoot.getPublicKey()).validate(senderCertificate, 31338); + throw new AssertionError(); + } catch (InvalidCertificateException e) { + // good + } + } + + public void testBadSignature() throws InvalidCertificateException, InvalidKeyException { + ECKeyPair serverKey = Curve.generateKeyPair(); + ECKeyPair key = Curve.generateKeyPair(); + + byte[] certificateBytes = SignalProtos.SenderCertificate.Certificate.newBuilder() + .setSender("+14152222222") + .setSenderDevice(1) + .setExpires(31337) + .setIdentityKey(ByteString.copyFrom(key.getPublicKey().serialize())) + .setSigner(getServerCertificate(serverKey)) + .build() + .toByteArray(); + + byte[] certificateSignature = Curve.calculateSignature(serverKey.getPrivateKey(), certificateBytes); + + for (int i=0;i