From 81212f213f7e3a697b8a0fbf833c80c067bee338 Mon Sep 17 00:00:00 2001 From: Carsten Wentzlow Date: Fri, 9 Dec 2011 18:25:41 +0100 Subject: [PATCH] adding restructured GPG4Browsers code - created a test suite available at test/index.html - fixed a bug in MD5 calculation --- LICENSE | 502 +++++++ plugins/chrome/background.html | 133 ++ plugins/chrome/contentscripts.js | 244 ++++ plugins/chrome/jquery.min.js | 18 + plugins/chrome/manifest.json | 32 + plugins/chrome/openpgp.html | 565 ++++++++ plugins/chrome/options.html | 446 ++++++ resources/jquery.min.js | 18 + src/ciphers/asymmetric/dsa.js | 151 ++ src/ciphers/asymmetric/elgamal.js | 48 + src/ciphers/asymmetric/jsbn.js | 589 ++++++++ src/ciphers/asymmetric/jsbn2.js | 714 ++++++++++ src/ciphers/asymmetric/rsa.js | 75 + src/ciphers/hash/md5.js | 374 +++++ src/ciphers/hash/ripe-md.js | 293 ++++ src/ciphers/hash/sha.js | 1221 +++++++++++++++++ src/ciphers/openpgp.cfb.js | 275 ++++ src/ciphers/openpgp.crypto.js | 458 +++++++ src/ciphers/symmetric/aes.js | 484 +++++++ src/ciphers/symmetric/blowfish.js | 393 ++++++ src/ciphers/symmetric/cast5.js | 547 ++++++++ src/ciphers/symmetric/dessrc.js | 208 +++ src/ciphers/symmetric/twofish.js | 302 ++++ src/config/openpgp.config.js | 78 ++ src/encoding/base64.js | 76 + src/encoding/openpgp.encoding.asciiarmor.js | 230 ++++ src/encoding/openpgp.encoding.js | 125 ++ src/openpgp.js | 357 +++++ src/openpgp.keyring.js | 227 +++ src/openpgp.msg.message.js | 135 ++ src/openpgp.msg.privatekey.js | 144 ++ src/openpgp.msg.publickey.js | 252 ++++ src/packet/openpgp.packet.compressed.js | 145 ++ src/packet/openpgp.packet.encrypteddata.js | 104 ++ ....packet.encryptedintegrityprotecteddata.js | 155 +++ .../openpgp.packet.encryptedsessionkey.js | 242 ++++ src/packet/openpgp.packet.js | 409 ++++++ src/packet/openpgp.packet.keymaterial.js | 689 ++++++++++ src/packet/openpgp.packet.literaldata.js | 99 ++ src/packet/openpgp.packet.marker.js | 64 + ...penpgp.packet.modificationdetectioncode.js | 79 ++ src/packet/openpgp.packet.onepasssignature.js | 118 ++ src/packet/openpgp.packet.signature.js | 755 ++++++++++ src/packet/openpgp.packet.userattribute.js | 153 +++ src/packet/openpgp.packet.userid.js | 333 +++++ src/type/openpgp.type.keyid.js | 47 + src/type/openpgp.type.mpi.js | 132 ++ src/type/openpgp.type.s2k.js | 98 ++ src/util/util.js | 217 +++ test/ciphers/hash/md5.js | 17 + test/ciphers/hash/ripe-md.js | 13 + test/ciphers/hash/sha.js | 26 + test/example-test.js | 8 + test/index.html | 151 ++ test/parser.html | 126 ++ test/unittest.js | 21 + webmail/googlemail.com/gmail.js | 70 + 57 files changed, 13985 insertions(+) create mode 100755 plugins/chrome/background.html create mode 100755 plugins/chrome/contentscripts.js create mode 100755 plugins/chrome/jquery.min.js create mode 100755 plugins/chrome/manifest.json create mode 100755 plugins/chrome/openpgp.html create mode 100755 plugins/chrome/options.html create mode 100755 resources/jquery.min.js create mode 100755 src/ciphers/asymmetric/dsa.js create mode 100755 src/ciphers/asymmetric/elgamal.js create mode 100755 src/ciphers/asymmetric/jsbn.js create mode 100755 src/ciphers/asymmetric/jsbn2.js create mode 100755 src/ciphers/asymmetric/rsa.js create mode 100755 src/ciphers/hash/md5.js create mode 100755 src/ciphers/hash/ripe-md.js create mode 100755 src/ciphers/hash/sha.js create mode 100755 src/ciphers/openpgp.cfb.js create mode 100755 src/ciphers/openpgp.crypto.js create mode 100755 src/ciphers/symmetric/aes.js create mode 100755 src/ciphers/symmetric/blowfish.js create mode 100755 src/ciphers/symmetric/cast5.js create mode 100755 src/ciphers/symmetric/dessrc.js create mode 100755 src/ciphers/symmetric/twofish.js create mode 100755 src/config/openpgp.config.js create mode 100755 src/encoding/base64.js create mode 100755 src/encoding/openpgp.encoding.asciiarmor.js create mode 100755 src/encoding/openpgp.encoding.js create mode 100755 src/openpgp.js create mode 100755 src/openpgp.keyring.js create mode 100755 src/openpgp.msg.message.js create mode 100755 src/openpgp.msg.privatekey.js create mode 100755 src/openpgp.msg.publickey.js create mode 100755 src/packet/openpgp.packet.compressed.js create mode 100755 src/packet/openpgp.packet.encrypteddata.js create mode 100755 src/packet/openpgp.packet.encryptedintegrityprotecteddata.js create mode 100755 src/packet/openpgp.packet.encryptedsessionkey.js create mode 100755 src/packet/openpgp.packet.js create mode 100755 src/packet/openpgp.packet.keymaterial.js create mode 100755 src/packet/openpgp.packet.literaldata.js create mode 100755 src/packet/openpgp.packet.marker.js create mode 100755 src/packet/openpgp.packet.modificationdetectioncode.js create mode 100755 src/packet/openpgp.packet.onepasssignature.js create mode 100755 src/packet/openpgp.packet.signature.js create mode 100755 src/packet/openpgp.packet.userattribute.js create mode 100755 src/packet/openpgp.packet.userid.js create mode 100755 src/type/openpgp.type.keyid.js create mode 100755 src/type/openpgp.type.mpi.js create mode 100755 src/type/openpgp.type.s2k.js create mode 100755 src/util/util.js create mode 100644 test/ciphers/hash/md5.js create mode 100644 test/ciphers/hash/ripe-md.js create mode 100644 test/ciphers/hash/sha.js create mode 100644 test/example-test.js create mode 100644 test/index.html create mode 100644 test/parser.html create mode 100644 test/unittest.js create mode 100755 webmail/googlemail.com/gmail.js diff --git a/LICENSE b/LICENSE index e69de29b..4362b491 100644 --- a/LICENSE +++ b/LICENSE @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +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 +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser 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 Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "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 +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY 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 +LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey 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 library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/plugins/chrome/background.html b/plugins/chrome/background.html new file mode 100755 index 00000000..afb49f28 --- /dev/null +++ b/plugins/chrome/background.html @@ -0,0 +1,133 @@ + + + + + + diff --git a/plugins/chrome/contentscripts.js b/plugins/chrome/contentscripts.js new file mode 100755 index 00000000..25101533 --- /dev/null +++ b/plugins/chrome/contentscripts.js @@ -0,0 +1,244 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + + +if (window.location.href.indexOf("https://mail.google.com/mail/?view=cm") == 0) { + // we are running in the compose window +} else { + // we are running in the normal interface + chrome.extension.sendRequest({account: document.getElementsByTagName("script")[4].text.split(",")[10].replace(/"/g,"").trim()}, function(response) {}); + +} + +var current_message_type = -1; +var current_message = null; + +/** + * searches the given text for a pgp message. If a message is available the openpgp message dialog is shown + * @param text text to be searched + */ +function find_openpgp(text) { + text = text.replace(/\r\n/g,"\n"); + if (document.location.hash != current_message) { + if (/-----BEGIN PGP MESSAGE-----/.test(text) && /-----END PGP MESSAGE-----/.test(text)) { + current_message= document.location.hash; + current_message_type = 0; + current_pgp_block = text.substring(text.indexOf("-----BEGIN PGP MESSAGE-----"), text.indexOf("-----END PGP MESSAGE-----")+25); + current_pgp_block = current_pgp_block.replace(/\n/g,"").replace(/
/g,"\n").replace(//g,""); + if (pgp_verifyCheckSum(current_pgp_block)) + show_pgp_alert(); + + } else if (/-----BEGIN PGP SIGNED MESSAGE-----/.test(text) && /-----END PGP SIGNATURE-----/.test(text)) { + current_message= document.location.hash; + current_message_type = 1; + current_pgp_block = text.substring(text.indexOf("-----BEGIN PGP SIGNED MESSAGE-----"), text.indexOf("-----END PGP SIGNATURE-----")+26); + current_pgp_block = current_pgp_block.replace(/\n/g,"").replace(/
/g,"\n").replace(//g,""); + if (pgp_verifyCheckSum(current_pgp_block.substring(current_pgp_block.indexOf("-----BEGIN PGP SIGNATURE-----")))) + show_pgp_alert(); + } else { + hide_pgp_alert(); + } + } +} + +var doc = null; + +/** + * call routine to open the openpgp.html page for handling a message + * @return null + */ +function start_pgp_dialog() { + //Gmail does not provide a generic way. to get message data out of the HTML interface so we parse the DOM + Gmail.getMail(function(msg) { + msg.action = 1; + chrome.extension.sendRequest(msg, function(response) { + // hide_pgp_alert(); // hide pgp alert after opening the openpgp window + }); + }); +} + +/** + * showing the pgp alert + * @return + */ +function show_pgp_alert() { + var div = document.createElement("div"); + var buttonyes = document.createElement("button"); + var buttonno = document.createElement("button"); + buttonyes.setAttribute("type", "submit"); + buttonyes.addEventListener("mousedown", function () { + var msg = start_pgp_dialog(); + }); + buttonno.setAttribute("type", "submit"); + buttonno.addEventListener("mousedown", function() { hide_pgp_alert(); }, true); + buttonyes.appendChild(document.createTextNode("Yes")); + buttonno.appendChild(document.createTextNode("No")); + div.setAttribute("id", "gpg4browsers_alert"); + div.setAttribute("style","position: fixed; top: 0px; width: 100%; background-color: #eeeeff; border-bottom: 1px solid #aaa;"); + if (current_message_type == 0) + div.appendChild(document.createTextNode("This mail is encrypted. Do you want to open it with GPG4Browsers?")); + else if (current_message_type == 1) + div.appendChild(document.createTextNode("This mail is signed. Do you want to open it with GPG4Browsers?")); + div.appendChild(buttonyes); + div.appendChild(buttonno); + document.body.appendChild(div); +}; + +/** + * hiding the pgp alert + * @return + */ +function hide_pgp_alert() { + if (document.getElementById("gpg4browsers_alert") != null) { + document.getElementById("gpg4browsers_alert").parentNode.removeChild(document.getElementById("gpg4browsers_alert")); + } +} + +/** + * background process timer to constantly check the displayed page for pgp messages + */ +window.setInterval(function() { + find_openpgp(document.body.innerHTML); + if (document.getElementById("canvas_frame") != null) + find_openpgp(document.getElementById("canvas_frame").contentDocument.body.innerHTML); +}, 1000); + + +/** + * verifies the checksum of an base64 encrypted pgp block + * @param text containing the base64 block and the base64 encoded checksum + * @return true if the checksum was correct, false otherwise + */ +function pgp_verifyCheckSum(text) { + var splittedtext = text.split('-----'); + var data = r2s(splittedtext[2].split('\n\n')[1].split("\n=")[0]); + var checksum = splittedtext[2].split('\n\n')[1].split("\n=")[1].replace(/\n/g,""); + var c = getCheckSum(data); + var d = checksum; + return c[0] == d[0] && c[1] == d[1] && c[2] == d[2]; +} + +/** + * calculates the checksum over a given block of data + * @param data block to be used + * @return a string containing the base64 encoded checksum + */ +function getCheckSum(data) { + var c = createcrc24(data); + var str = "" + String.fromCharCode(c >> 16)+ + String.fromCharCode((c >> 8) & 0xFF)+ + String.fromCharCode(c & 0xFF); + return s2r(str); +} + + +/** + * calculation routine for a CRC-24 checksum + * @param data + * @return + */ +function createcrc24 (data) { + var crc = 0xB704CE; + var i; + var mypos = 0; + var len = data.length; + while (len--) { + crc ^= (data[mypos++].charCodeAt()) << 16; + for (i = 0; i < 8; i++) { + crc <<= 1; + if (crc & 0x1000000) + crc ^= 0x1864CFB; + } + } + return crc & 0xFFFFFF; +} + +// base64 implementation + +var b64s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + +/** + * Converting Base64 data to a string + * @param t base64 encoded data string + * @return data string + */ +function r2s(t) { + var c, n; + var r = '', s = 0, a = 0; + var tl = t.length; + + for (n = 0; n < tl; n++) { + c = b64s.indexOf(t.charAt(n)); + if (c >= 0) { + if (s) + r += String.fromCharCode(a | (c >> (6 - s)) & 255); + s = (s + 2) & 7; + a = (c << s) & 255; + } + } + return r; +} + +/** + * Converting a data string to a base64 encoded string + * @param t data string + * @return base64 encoded data string + */ +function s2r(t) { + var a, c, n; + var r = '', l = 0, s = 0; + var tl = t.length; + + for (n = 0; n < tl; n++) { + c = t.charCodeAt(n); + if (s == 0) { + r += b64s.charAt((c >> 2) & 63); + a = (c & 3) << 4; + } else if (s == 1) { + r += b64s.charAt((a | (c >> 4) & 15)); + a = (c & 15) << 2; + } else if (s == 2) { + r += b64s.charAt(a | ((c >> 6) & 3)); + l += 1; + if ((l % 60) == 0) + r += "\n"; + r += b64s.charAt(c & 63); + } + l += 1; + if ((l % 60) == 0) + r += "\n"; + + s += 1; + if (s == 3) + s = 0; + } + if (s > 0) { + r += b64s.charAt(a); + l += 1; + if ((l % 60) == 0) + r += "\n"; + r += '='; + l += 1; + } + if (s == 1) { + if ((l % 60) == 0) + r += "\n"; + r += '='; + } + + return r; +} diff --git a/plugins/chrome/jquery.min.js b/plugins/chrome/jquery.min.js new file mode 100755 index 00000000..b2ac1747 --- /dev/null +++ b/plugins/chrome/jquery.min.js @@ -0,0 +1,18 @@ +/*! + * jQuery JavaScript Library v1.6.1 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Thu May 12 15:04:36 2011 -0400 + */ +(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!cj[a]){var b=f("<"+a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),c.body.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write("");b=cl.createElement(a),cl.body.appendChild(b),d=f.css(b,"display"),c.body.removeChild(ck)}cj[a]=d}return cj[a]}function cu(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function ct(){cq=b}function cs(){setTimeout(ct,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g=0===c})}function W(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function O(a,b){return(a&&a!=="*"?a+".":"")+b.replace(A,"`").replace(B,"&")}function N(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;ic)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function L(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function F(){return!0}function E(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(j,"$1-$2").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(g){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function H(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(H,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=d.userAgent,x,y,z,A=Object.prototype.toString,B=Object.prototype.hasOwnProperty,C=Array.prototype.push,D=Array.prototype.slice,E=String.prototype.trim,F=Array.prototype.indexOf,G={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6.1",length:0,size:function(){return this.length},toArray:function(){return D.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?C.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),y.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(D.apply(this,arguments),"slice",D.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:C,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;y.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!y){y=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",z,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",z),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&H()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):G[A.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;if(a.constructor&&!B.call(a,"constructor")&&!B.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a);return c===b||B.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(b,c,d){a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),d=c.documentElement,(!d||!d.nodeName||d.nodeName==="parsererror")&&e.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;c
a",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};f=c.createElement("select"),g=f.appendChild(c.createElement("option")),h=a.getElementsByTagName("input")[0],j={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55$/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},h.checked=!0,j.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,j.optDisabled=!g.disabled;try{delete a.test}catch(s){j.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function b(){j.noCloneEvent=!1,a.detachEvent("onclick",b)}),a.cloneNode(!0).fireEvent("onclick")),h=c.createElement("input"),h.value="t",h.setAttribute("type","radio"),j.radioValue=h.value==="t",h.setAttribute("checked","checked"),a.appendChild(h),k=c.createDocumentFragment(),k.appendChild(a.firstChild),j.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",l=c.createElement("body"),m={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"};for(q in m)l.style[q]=m[q];l.appendChild(a),b.insertBefore(l,b.firstChild),j.appendChecked=h.checked,j.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,j.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
",j.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
t
",n=a.getElementsByTagName("td"),r=n[0].offsetHeight===0,n[0].style.display="",n[1].style.display="none",j.reliableHiddenOffsets=r&&n[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(i=c.createElement("div"),i.style.width="0",i.style.marginRight="0",a.appendChild(i),j.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(i,null)||{marginRight:0}).marginRight,10)||0)===0),l.innerHTML="",b.removeChild(l);if(a.attachEvent)for(q in{submit:1,change:1,focusin:1})p="on"+q,r=p in a,r||(a.setAttribute(p,"return;"),r=typeof a[p]=="function"),j[q+"Bubbles"]=r;return j}(),f.boxModel=f.support.boxModel;var i=/^(?:\{.*\}|\[.*\])$/,j=/([a-z])([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!l(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g=f.expando,h=typeof c=="string",i,j=a.nodeType,k=j?f.cache:a,l=j?a[f.expando]:a[f.expando]&&f.expando;if((!l||e&&l&&!k[l][g])&&h&&d===b)return;l||(j?a[f.expando]=l=++f.uuid:l=f.expando),k[l]||(k[l]={},j||(k[l].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?k[l][g]=f.extend(k[l][g],c):k[l]=f.extend(k[l],c);i=k[l],e&&(i[g]||(i[g]={}),i=i[g]),d!==b&&(i[f.camelCase(c)]=d);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[f.camelCase(c)]:i}},removeData:function(b,c,d){if(!!f.acceptData(b)){var e=f.expando,g=b.nodeType,h=g?f.cache:b,i=g?b[f.expando]:f.expando;if(!h[i])return;if(c){var j=d?h[i][e]:h[i];if(j){delete j[c];if(!l(j))return}}if(d){delete h[i][e];if(!l(h[i]))return}var k=h[i][e];f.support.deleteExpando||h!=a?delete h[i]:h[i]=null,k?(h[i]={},g||(h[i].toJSON=f.noop),h[i][e]=k):g&&(f.support.deleteExpando?delete b[f.expando]:b.removeAttribute?b.removeAttribute(f.expando):b[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d=null;if(typeof a=="undefined"){if(this.length){d=f.data(this[0]);if(this[0].nodeType===1){var e=this[0].attributes,g;for(var h=0,i=e.length;h-1)return!0;return!1},val:function(a){var c,d,e=this[0];if(!arguments.length){if(e){c=f.valHooks[e.nodeName.toLowerCase()]||f.valHooks[e.type];if(c&&"get"in c&&(d=c.get(e,"value"))!==b)return d;return(e.value||"").replace(p,"")}return b}var g=f.isFunction(a);return this.each(function(d){var e=f(this),h;if(this.nodeType===1){g?h=a.call(this,d,e.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c=a.selectedIndex,d=[],e=a.options,g=a.type==="select-one";if(c<0)return null;for(var h=g?c:0,i=g?c+1:e.length;h=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attrFix:{tabindex:"tabIndex"},attr:function(a,c,d,e){var g=a.nodeType;if(!a||g===3||g===8||g===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);var h,i,j=g!==1||!f.isXMLDoc(a);c=j&&f.attrFix[c]||c,i=f.attrHooks[c],i||(!t.test(c)||typeof d!="boolean"&&d!==b&&d.toLowerCase()!==c.toLowerCase()?v&&(f.nodeName(a,"form")||u.test(c))&&(i=v):i=w);if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(i&&"set"in i&&j&&(h=i.set(a,d,c))!==b)return h;a.setAttribute(c,""+d);return d}if(i&&"get"in i&&j)return i.get(a,c);h=a.getAttribute(c);return h===null?b:h},removeAttr:function(a,b){var c;a.nodeType===1&&(b=f.attrFix[b]||b,f.support.getSetAttribute?a.removeAttribute(b):(f.attr(a,b,""),a.removeAttributeNode(a.getAttributeNode(b))),t.test(b)&&(c=f.propFix[b]||b)in a&&(a[c]=!1))},attrHooks:{type:{set:function(a,b){if(q.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},tabIndex:{get:function(a){var c=a.getAttributeNode("tabIndex");return c&&c.specified?parseInt(c.value,10):r.test(a.nodeName)||s.test(a.nodeName)&&a.href?0:b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e=a.nodeType;if(!a||e===3||e===8||e===2)return b;var g,h,i=e!==1||!f.isXMLDoc(a);c=i&&f.propFix[c]||c,h=f.propHooks[c];return d!==b?h&&"set"in h&&(g=h.set(a,d,c))!==b?g:a[c]=d:h&&"get"in h&&(g=h.get(a,c))!==b?g:a[c]},propHooks:{}}),w={get:function(a,c){return a[f.propFix[c]||c]?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=b),a.setAttribute(c,c.toLowerCase()));return c}},f.attrHooks.value={get:function(a,b){if(v&&f.nodeName(a,"button"))return v.get(a,b);return a.value},set:function(a,b,c){if(v&&f.nodeName(a,"button"))return v.set(a,b,c);a.value=b}},f.support.getSetAttribute||(f.attrFix=f.propFix,v=f.attrHooks.name=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&d.nodeValue!==""?d.nodeValue:b},set:function(a,b,c){var d=a.getAttributeNode(c);if(d){d.nodeValue=b;return b}}},f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})})),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}})),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var x=Object.prototype.hasOwnProperty,y=/\.(.*)$/,z=/^(?:textarea|input|select)$/i,A=/\./g,B=/ /g,C=/[^\w\s.|`]/g,D=function(a){return a.replace(C,"\\$&")};f.event={add:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){if(d===!1)d=E;else if(!d)return;var g,h;d.handler&&(g=d,d=g.handler),d.guid||(d.guid=f.guid++);var i=f._data(a);if(!i)return;var j=i.events,k=i.handle;j||(i.events=j={}),k||(i.handle=k=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.handle.apply(k.elem,arguments):b}),k.elem=a,c=c.split(" ");var l,m=0,n;while(l=c[m++]){h=g?f.extend({},g):{handler:d,data:e},l.indexOf(".")>-1?(n=l.split("."),l=n.shift(),h.namespace=n.slice(0).sort().join(".")):(n=[],h.namespace=""),h.type=l,h.guid||(h.guid=d.guid);var o=j[l],p=f.event.special[l]||{};if(!o){o=j[l]=[];if(!p.setup||p.setup.call(a,e,n,k)===!1)a.addEventListener?a.addEventListener(l,k,!1):a.attachEvent&&a.attachEvent("on"+l,k)}p.add&&(p.add.call(a,h),h.handler.guid||(h.handler.guid=d.guid)),o.push(h),f.event.global[l]=!0}a=null}},global:{},remove:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){d===!1&&(d=E);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=f.hasData(a)&&f._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(d=c.handler,c=c.type);if(!c||typeof c=="string"&&c.charAt(0)==="."){c=c||"";for(h in t)f.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+f.map(m.slice(0).sort(),D).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!d){for(j=0;j=0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem +)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},K=function(c){var d=c.target,e,g;if(!!z.test(d.nodeName)&&!d.readOnly){e=f._data(d,"_change_data"),g=J(d),(c.type!=="focusout"||d.type!=="radio")&&f._data(d,"_change_data",g);if(e===b||g===e)return;if(e!=null||g)c.type="change",c.liveFired=b,f.event.trigger(c,arguments[1],d)}};f.event.special.change={filters:{focusout:K,beforedeactivate:K,click:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(c==="radio"||c==="checkbox"||f.nodeName(b,"select"))&&K.call(this,a)},keydown:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(a.keyCode===13&&!f.nodeName(b,"textarea")||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&K.call(this,a)},beforeactivate:function(a){var b=a.target;f._data(b,"_change_data",J(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in I)f.event.add(this,c+".specialChange",I[c]);return z.test(this.nodeName)},teardown:function(a){f.event.remove(this,".specialChange");return z.test(this.nodeName)}},I=f.event.special.change.filters,I.focus=I.beforeactivate}f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){function e(a){var c=f.event.fix(a);c.type=b,c.originalEvent={},f.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var d=0;f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.each(["bind","one"],function(a,c){f.fn[c]=function(a,d,e){var g;if(typeof a=="object"){for(var h in a)this[c](h,d,a[h],e);return this}if(arguments.length===2||d===!1)e=d,d=b;c==="one"?(g=function(a){f(this).unbind(a,g);return e.apply(this,arguments)},g.guid=e.guid||f.guid++):g=e;if(a==="unload"&&c!=="one")this.one(a,d,e);else for(var i=0,j=this.length;i0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d=0,e=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,f,g){f=f||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return f;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(e.call(n)==="[object Array]")if(!u)f.push.apply(f,n);else if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&f.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&f.push(j[t]);else p(n,f);o&&(k(o,h,f,g),k.uniqueSort(f));return f};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=d++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(e.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var f=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g0)for(h=g;h0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(d=0,e=a.length;d-1:f(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=U.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a=="string")return f.inArray(this[0],a?f(a):this.parent().children());return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(W(c[0])||W(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=T.call(arguments);P.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!V[a]?f.unique(e):e,(this.length>1||R.test(d))&&Q.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var Y=/ jQuery\d+="(?:\d+|null)"/g,Z=/^\s+/,$=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,_=/<([\w:]+)/,ba=/",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
","
"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Y,""):null;if(typeof a=="string"&&!bc.test(a)&&(f.support.leadingWhitespace||!Z.test(a))&&!bg[(_.exec(a)||["",""])[1].toLowerCase()]){a=a.replace($,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bj(a,d),e=bk(a),g=bk(d);for(h=0;e[h];++h)bj(e[h],g[h])}if(b){bi(a,d);if(c){e=bk(a),g=bk(d);for(h=0;e[h];++h)bi(e[h],g[h])}}return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument|| +b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!bb.test(k))k=b.createTextNode(k);else{k=k.replace($,"<$1>");var l=(_.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=ba.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&Z.test(k)&&o.insertBefore(b.createTextNode(Z.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bp.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle;c.zoom=1;var e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.filter=bo.test(g)?g.replace(bo,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,c){var d,e,g;c=c.replace(br,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bs.test(d)&&bt.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bE=/%20/g,bF=/\[\]$/,bG=/\r?\n/g,bH=/#.*$/,bI=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bJ=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bK=/^(?:about|app|app\-storage|.+\-extension|file|widget):$/,bL=/^(?:GET|HEAD)$/,bM=/^\/\//,bN=/\?/,bO=/)<[^<]*)*<\/script>/gi,bP=/^(?:select|textarea)/i,bQ=/\s+/,bR=/([?&])_=[^&]*/,bS=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bT=f.fn.load,bU={},bV={},bW,bX;try{bW=e.href}catch(bY){bW=c.createElement("a"),bW.href="",bW=bW.href}bX=bS.exec(bW.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bT)return bT.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bO,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bP.test(this.nodeName)||bJ.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bG,"\r\n")}}):{name:b.name,value:c.replace(bG,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?f.extend(!0,a,f.ajaxSettings,b):(b=a,a=f.extend(!0,f.ajaxSettings,b));for(var c in{context:1,url:1})c in b?a[c]=b[c]:c in f.ajaxSettings&&(a[c]=f.ajaxSettings[c]);return a},ajaxSettings:{url:bW,isLocal:bK.test(bX[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML}},ajaxPrefilter:bZ(bU),ajaxTransport:bZ(bV),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a?4:0;var o,r,u,w=l?ca(d,v,l):b,x,y;if(a>=200&&a<300||a===304){if(d.ifModified){if(x=v.getResponseHeader("Last-Modified"))f.lastModified[k]=x;if(y=v.getResponseHeader("Etag"))f.etag[k]=y}if(a===304)c="notmodified",o=!0;else try{r=cb(d,w),c="success",o=!0}catch(z){c="parsererror",u=z}}else{u=c;if(!c||a)c="error",a<0&&(a=0)}v.status=a,v.statusText=c,o?h.resolveWith(e,[r,c,v]):h.rejectWith(e,[v,c,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.resolveWith(e,[v,c]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f._Deferred(),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bI.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.done,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bH,"").replace(bM,bX[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bQ),d.crossDomain==null&&(r=bS.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bX[1]&&r[2]==bX[2]&&(r[3]||(r[1]==="http:"?80:443))==(bX[3]||(bX[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bU,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bL.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bN.test(d.url)?"&":"?")+d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bR,"$1_="+x);d.url=y+(y===d.url?(bN.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", */*; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bV,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){status<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bE,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq,cr=a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g=e.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),e.animatedProperties[this.prop]=!0;for(g in e.animatedProperties)e.animatedProperties[g]!==!0&&(c=!1);if(c){e.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){d.style["overflow"+b]=e.overflow[a]}),e.hide&&f(d).hide();if(e.hide||e.show)for(var i in e.animatedProperties)f.style(d,i,e.orig[i]);e.complete.call(d)}return!1}e.duration==Infinity?this.now=b:(h=b-this.startTime,this.state=h/e.duration,this.pos=f.easing[e.animatedProperties[this.prop]](this.state,h,0,1,e.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){for(var a=f.timers,b=0;b
";f.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),d=b.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,this.doesNotAddBorder=e.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,e.style.position="fixed",e.style.top="20px",this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),f.offset.initialize=f.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.offset.initialize(),f.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){return this[0]?parseFloat(f.css(this[0],d,"padding")):null},f.fn["outer"+c]=function(a){return this[0]?parseFloat(f.css(this[0],d,a?"margin":"border")):null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c];return e.document.compatMode==="CSS1Compat"&&g||e.document.body["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var h=f.css(e,d),i=parseFloat(h);return f.isNaN(i)?h:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window); \ No newline at end of file diff --git a/plugins/chrome/manifest.json b/plugins/chrome/manifest.json new file mode 100755 index 00000000..9640f4e7 --- /dev/null +++ b/plugins/chrome/manifest.json @@ -0,0 +1,32 @@ +{ + "name" : "GPG4Browsers", + "version" : "0.1", + "description" : "An OpenPGP implementation in javascript", + "background_page" : "background.html", + "options_page": "options.html", + "page_action" : + { + "default_icon" : "images/icons/logo.png", + "default_title" : "Create encrypted Message" + }, + "content_scripts" : [ + { + "matches" : [ + "https://mail.google.com/*" + ], + "js" : ["jquery.min.js", "contentscripts.js", "gmail.js"], + "run_at" : "document_idle", + "all_frames" : false + } + ], + "incoginto": "split", + "permissions": [ + "https://mail.google.com/*", + "http://keyserver.linux.it/*", + "unlimitedStorage" + ], + "icons" : { + "48" : "images/icons/logo.png", + "128" : "images/icons/logo.png" + } +} diff --git a/plugins/chrome/openpgp.html b/plugins/chrome/openpgp.html new file mode 100755 index 00000000..e194e171 --- /dev/null +++ b/plugins/chrome/openpgp.html @@ -0,0 +1,565 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +GPG4Browsers + + + + + + + + + + + + + + +
From:
To:
CC:
BCC:
Subject:
+ + + + + + + +
+ + + + +
Sign message
Encrypt message
From:
Subject:
+ +
+Enter your Password for key :
+ +
+
+
+
+ +
+
+
+ + +
+
+
+
+ + + diff --git a/plugins/chrome/options.html b/plugins/chrome/options.html new file mode 100755 index 00000000..ab6161f8 --- /dev/null +++ b/plugins/chrome/options.html @@ -0,0 +1,446 @@ + + + + GPG4Browsers - PGP Message Options + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + diff --git a/resources/jquery.min.js b/resources/jquery.min.js new file mode 100755 index 00000000..b2ac1747 --- /dev/null +++ b/resources/jquery.min.js @@ -0,0 +1,18 @@ +/*! + * jQuery JavaScript Library v1.6.1 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Thu May 12 15:04:36 2011 -0400 + */ +(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cv(a){if(!cj[a]){var b=f("<"+a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),c.body.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write("");b=cl.createElement(a),cl.body.appendChild(b),d=f.css(b,"display"),c.body.removeChild(ck)}cj[a]=d}return cj[a]}function cu(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function ct(){cq=b}function cs(){setTimeout(ct,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g=0===c})}function W(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function O(a,b){return(a&&a!=="*"?a+".":"")+b.replace(A,"`").replace(B,"&")}function N(a){var b,c,d,e,g,h,i,j,k,l,m,n,o,p=[],q=[],r=f._data(this,"events");if(!(a.liveFired===this||!r||!r.live||a.target.disabled||a.button&&a.type==="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var s=r.live.slice(0);for(i=0;ic)break;a.currentTarget=e.elem,a.data=e.handleObj.data,a.handleObj=e.handleObj,o=e.handleObj.origHandler.apply(e.elem,arguments);if(o===!1||a.isPropagationStopped()){c=e.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function L(a,c,d){var e=f.extend({},d[0]);e.type=a,e.originalEvent={},e.liveFired=b,f.event.handle.call(c,e),e.isDefaultPrevented()&&d[0].preventDefault()}function F(){return!0}function E(){return!1}function m(a,c,d){var e=c+"defer",g=c+"queue",h=c+"mark",i=f.data(a,e,b,!0);i&&(d==="queue"||!f.data(a,g,b,!0))&&(d==="mark"||!f.data(a,h,b,!0))&&setTimeout(function(){!f.data(a,g,b,!0)&&!f.data(a,h,b,!0)&&(f.removeData(a,e,!0),i.resolve())},0)}function l(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function k(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(j,"$1-$2").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNaN(d)?i.test(d)?f.parseJSON(d):d:parseFloat(d)}catch(g){}f.data(a,c,d)}else d=b}return d}var c=a.document,d=a.navigator,e=a.location,f=function(){function H(){if(!e.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(H,1);return}e.ready()}}var e=function(a,b){return new e.fn.init(a,b,h)},f=a.jQuery,g=a.$,h,i=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/\d/,n=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,o=/^[\],:{}\s]*$/,p=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,q=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,r=/(?:^|:|,)(?:\s*\[)+/g,s=/(webkit)[ \/]([\w.]+)/,t=/(opera)(?:.*version)?[ \/]([\w.]+)/,u=/(msie) ([\w.]+)/,v=/(mozilla)(?:.*? rv:([\w.]+))?/,w=d.userAgent,x,y,z,A=Object.prototype.toString,B=Object.prototype.hasOwnProperty,C=Array.prototype.push,D=Array.prototype.slice,E=String.prototype.trim,F=Array.prototype.indexOf,G={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=n.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.6.1",length:0,size:function(){return this.length},toArray:function(){return D.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?C.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),y.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(D.apply(this,arguments),"slice",D.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:C,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;y.resolveWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!y){y=e._Deferred();if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",z,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",z),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&H()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a&&typeof a=="object"&&"setInterval"in a},isNaN:function(a){return a==null||!m.test(a)||isNaN(a)},type:function(a){return a==null?String(a):G[A.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;if(a.constructor&&!B.call(a,"constructor")&&!B.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a);return c===b||B.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(o.test(b.replace(p,"@").replace(q,"]").replace(r,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(b,c,d){a.DOMParser?(d=new DOMParser,c=d.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),d=c.documentElement,(!d||!d.nodeName||d.nodeName==="parsererror")&&e.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?h.call(arguments,0):c,--e||g.resolveWith(g,h.call(b,0))}}var b=arguments,c=0,d=b.length,e=d,g=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred();if(d>1){for(;c
a",d=a.getElementsByTagName("*"),e=a.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};f=c.createElement("select"),g=f.appendChild(c.createElement("option")),h=a.getElementsByTagName("input")[0],j={leadingWhitespace:a.firstChild.nodeType===3,tbody:!a.getElementsByTagName("tbody").length,htmlSerialize:!!a.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55$/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:a.className!=="t",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0},h.checked=!0,j.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,j.optDisabled=!g.disabled;try{delete a.test}catch(s){j.deleteExpando=!1}!a.addEventListener&&a.attachEvent&&a.fireEvent&&(a.attachEvent("onclick",function b(){j.noCloneEvent=!1,a.detachEvent("onclick",b)}),a.cloneNode(!0).fireEvent("onclick")),h=c.createElement("input"),h.value="t",h.setAttribute("type","radio"),j.radioValue=h.value==="t",h.setAttribute("checked","checked"),a.appendChild(h),k=c.createDocumentFragment(),k.appendChild(a.firstChild),j.checkClone=k.cloneNode(!0).cloneNode(!0).lastChild.checked,a.innerHTML="",a.style.width=a.style.paddingLeft="1px",l=c.createElement("body"),m={visibility:"hidden",width:0,height:0,border:0,margin:0,background:"none"};for(q in m)l.style[q]=m[q];l.appendChild(a),b.insertBefore(l,b.firstChild),j.appendChecked=h.checked,j.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,j.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
",j.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
t
",n=a.getElementsByTagName("td"),r=n[0].offsetHeight===0,n[0].style.display="",n[1].style.display="none",j.reliableHiddenOffsets=r&&n[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(i=c.createElement("div"),i.style.width="0",i.style.marginRight="0",a.appendChild(i),j.reliableMarginRight=(parseInt((c.defaultView.getComputedStyle(i,null)||{marginRight:0}).marginRight,10)||0)===0),l.innerHTML="",b.removeChild(l);if(a.attachEvent)for(q in{submit:1,change:1,focusin:1})p="on"+q,r=p in a,r||(a.setAttribute(p,"return;"),r=typeof a[p]=="function"),j[q+"Bubbles"]=r;return j}(),f.boxModel=f.support.boxModel;var i=/^(?:\{.*\}|\[.*\])$/,j=/([a-z])([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!l(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g=f.expando,h=typeof c=="string",i,j=a.nodeType,k=j?f.cache:a,l=j?a[f.expando]:a[f.expando]&&f.expando;if((!l||e&&l&&!k[l][g])&&h&&d===b)return;l||(j?a[f.expando]=l=++f.uuid:l=f.expando),k[l]||(k[l]={},j||(k[l].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?k[l][g]=f.extend(k[l][g],c):k[l]=f.extend(k[l],c);i=k[l],e&&(i[g]||(i[g]={}),i=i[g]),d!==b&&(i[f.camelCase(c)]=d);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[f.camelCase(c)]:i}},removeData:function(b,c,d){if(!!f.acceptData(b)){var e=f.expando,g=b.nodeType,h=g?f.cache:b,i=g?b[f.expando]:f.expando;if(!h[i])return;if(c){var j=d?h[i][e]:h[i];if(j){delete j[c];if(!l(j))return}}if(d){delete h[i][e];if(!l(h[i]))return}var k=h[i][e];f.support.deleteExpando||h!=a?delete h[i]:h[i]=null,k?(h[i]={},g||(h[i].toJSON=f.noop),h[i][e]=k):g&&(f.support.deleteExpando?delete b[f.expando]:b.removeAttribute?b.removeAttribute(f.expando):b[f.expando]=null)}},_data:function(a,b,c){return f.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=f.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),f.fn.extend({data:function(a,c){var d=null;if(typeof a=="undefined"){if(this.length){d=f.data(this[0]);if(this[0].nodeType===1){var e=this[0].attributes,g;for(var h=0,i=e.length;h-1)return!0;return!1},val:function(a){var c,d,e=this[0];if(!arguments.length){if(e){c=f.valHooks[e.nodeName.toLowerCase()]||f.valHooks[e.type];if(c&&"get"in c&&(d=c.get(e,"value"))!==b)return d;return(e.value||"").replace(p,"")}return b}var g=f.isFunction(a);return this.each(function(d){var e=f(this),h;if(this.nodeType===1){g?h=a.call(this,d,e.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.nodeName.toLowerCase()]||f.valHooks[this.type];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c=a.selectedIndex,d=[],e=a.options,g=a.type==="select-one";if(c<0)return null;for(var h=g?c:0,i=g?c+1:e.length;h=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attrFix:{tabindex:"tabIndex"},attr:function(a,c,d,e){var g=a.nodeType;if(!a||g===3||g===8||g===2)return b;if(e&&c in f.attrFn)return f(a)[c](d);if(!("getAttribute"in a))return f.prop(a,c,d);var h,i,j=g!==1||!f.isXMLDoc(a);c=j&&f.attrFix[c]||c,i=f.attrHooks[c],i||(!t.test(c)||typeof d!="boolean"&&d!==b&&d.toLowerCase()!==c.toLowerCase()?v&&(f.nodeName(a,"form")||u.test(c))&&(i=v):i=w);if(d!==b){if(d===null){f.removeAttr(a,c);return b}if(i&&"set"in i&&j&&(h=i.set(a,d,c))!==b)return h;a.setAttribute(c,""+d);return d}if(i&&"get"in i&&j)return i.get(a,c);h=a.getAttribute(c);return h===null?b:h},removeAttr:function(a,b){var c;a.nodeType===1&&(b=f.attrFix[b]||b,f.support.getSetAttribute?a.removeAttribute(b):(f.attr(a,b,""),a.removeAttributeNode(a.getAttributeNode(b))),t.test(b)&&(c=f.propFix[b]||b)in a&&(a[c]=!1))},attrHooks:{type:{set:function(a,b){if(q.test(a.nodeName)&&a.parentNode)f.error("type property can't be changed");else if(!f.support.radioValue&&b==="radio"&&f.nodeName(a,"input")){var c=a.value;a.setAttribute("type",b),c&&(a.value=c);return b}}},tabIndex:{get:function(a){var c=a.getAttributeNode("tabIndex");return c&&c.specified?parseInt(c.value,10):r.test(a.nodeName)||s.test(a.nodeName)&&a.href?0:b}}},propFix:{tabindex:"tabIndex",readonly:"readOnly","for":"htmlFor","class":"className",maxlength:"maxLength",cellspacing:"cellSpacing",cellpadding:"cellPadding",rowspan:"rowSpan",colspan:"colSpan",usemap:"useMap",frameborder:"frameBorder",contenteditable:"contentEditable"},prop:function(a,c,d){var e=a.nodeType;if(!a||e===3||e===8||e===2)return b;var g,h,i=e!==1||!f.isXMLDoc(a);c=i&&f.propFix[c]||c,h=f.propHooks[c];return d!==b?h&&"set"in h&&(g=h.set(a,d,c))!==b?g:a[c]=d:h&&"get"in h&&(g=h.get(a,c))!==b?g:a[c]},propHooks:{}}),w={get:function(a,c){return a[f.propFix[c]||c]?c.toLowerCase():b},set:function(a,b,c){var d;b===!1?f.removeAttr(a,c):(d=f.propFix[c]||c,d in a&&(a[d]=b),a.setAttribute(c,c.toLowerCase()));return c}},f.attrHooks.value={get:function(a,b){if(v&&f.nodeName(a,"button"))return v.get(a,b);return a.value},set:function(a,b,c){if(v&&f.nodeName(a,"button"))return v.set(a,b,c);a.value=b}},f.support.getSetAttribute||(f.attrFix=f.propFix,v=f.attrHooks.name=f.valHooks.button={get:function(a,c){var d;d=a.getAttributeNode(c);return d&&d.nodeValue!==""?d.nodeValue:b},set:function(a,b,c){var d=a.getAttributeNode(c);if(d){d.nodeValue=b;return b}}},f.each(["width","height"],function(a,b){f.attrHooks[b]=f.extend(f.attrHooks[b],{set:function(a,c){if(c===""){a.setAttribute(b,"auto");return c}}})})),f.support.hrefNormalized||f.each(["href","src","width","height"],function(a,c){f.attrHooks[c]=f.extend(f.attrHooks[c],{get:function(a){var d=a.getAttribute(c,2);return d===null?b:d}})}),f.support.style||(f.attrHooks.style={get:function(a){return a.style.cssText.toLowerCase()||b},set:function(a,b){return a.style.cssText=""+b}}),f.support.optSelected||(f.propHooks.selected=f.extend(f.propHooks.selected,{get:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}})),f.support.checkOn||f.each(["radio","checkbox"],function(){f.valHooks[this]={get:function(a){return a.getAttribute("value")===null?"on":a.value}}}),f.each(["radio","checkbox"],function(){f.valHooks[this]=f.extend(f.valHooks[this],{set:function(a,b){if(f.isArray(b))return a.checked=f.inArray(f(a).val(),b)>=0}})});var x=Object.prototype.hasOwnProperty,y=/\.(.*)$/,z=/^(?:textarea|input|select)$/i,A=/\./g,B=/ /g,C=/[^\w\s.|`]/g,D=function(a){return a.replace(C,"\\$&")};f.event={add:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){if(d===!1)d=E;else if(!d)return;var g,h;d.handler&&(g=d,d=g.handler),d.guid||(d.guid=f.guid++);var i=f._data(a);if(!i)return;var j=i.events,k=i.handle;j||(i.events=j={}),k||(i.handle=k=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.handle.apply(k.elem,arguments):b}),k.elem=a,c=c.split(" ");var l,m=0,n;while(l=c[m++]){h=g?f.extend({},g):{handler:d,data:e},l.indexOf(".")>-1?(n=l.split("."),l=n.shift(),h.namespace=n.slice(0).sort().join(".")):(n=[],h.namespace=""),h.type=l,h.guid||(h.guid=d.guid);var o=j[l],p=f.event.special[l]||{};if(!o){o=j[l]=[];if(!p.setup||p.setup.call(a,e,n,k)===!1)a.addEventListener?a.addEventListener(l,k,!1):a.attachEvent&&a.attachEvent("on"+l,k)}p.add&&(p.add.call(a,h),h.handler.guid||(h.handler.guid=d.guid)),o.push(h),f.event.global[l]=!0}a=null}},global:{},remove:function(a,c,d,e){if(a.nodeType!==3&&a.nodeType!==8){d===!1&&(d=E);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=f.hasData(a)&&f._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(d=c.handler,c=c.type);if(!c||typeof c=="string"&&c.charAt(0)==="."){c=c||"";for(h in t)f.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+f.map(m.slice(0).sort(),D).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!d){for(j=0;j=0&&(h=h.slice(0,-1),j=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if(!!e&&!f.event.customEvent[h]||!!f.event.global[h]){c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.exclusive=j,c.namespace=i.join("."),c.namespace_re=new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)");if(g||!e)c.preventDefault(),c.stopPropagation();if(!e){f.each(f.cache,function(){var a=f.expando,b=this[a];b&&b.events&&b.events[h]&&f.event.trigger(c,d,b.handle.elem +)});return}if(e.nodeType===3||e.nodeType===8)return;c.result=b,c.target=e,d=d?f.makeArray(d):[],d.unshift(c);var k=e,l=h.indexOf(":")<0?"on"+h:"";do{var m=f._data(k,"handle");c.currentTarget=k,m&&m.apply(k,d),l&&f.acceptData(k)&&k[l]&&k[l].apply(k,d)===!1&&(c.result=!1,c.preventDefault()),k=k.parentNode||k.ownerDocument||k===c.target.ownerDocument&&a}while(k&&!c.isPropagationStopped());if(!c.isDefaultPrevented()){var n,o=f.event.special[h]||{};if((!o._default||o._default.call(e.ownerDocument,c)===!1)&&(h!=="click"||!f.nodeName(e,"a"))&&f.acceptData(e)){try{l&&e[h]&&(n=e[l],n&&(e[l]=null),f.event.triggered=h,e[h]())}catch(p){}n&&(e[l]=n),f.event.triggered=b}}return c.result}},handle:function(c){c=f.event.fix(c||a.event);var d=((f._data(this,"events")||{})[c.type]||[]).slice(0),e=!c.exclusive&&!c.namespace,g=Array.prototype.slice.call(arguments,0);g[0]=c,c.currentTarget=this;for(var h=0,i=d.length;h-1?f.map(a.options,function(a){return a.selected}).join("-"):"":f.nodeName(a,"select")&&(c=a.selectedIndex);return c},K=function(c){var d=c.target,e,g;if(!!z.test(d.nodeName)&&!d.readOnly){e=f._data(d,"_change_data"),g=J(d),(c.type!=="focusout"||d.type!=="radio")&&f._data(d,"_change_data",g);if(e===b||g===e)return;if(e!=null||g)c.type="change",c.liveFired=b,f.event.trigger(c,arguments[1],d)}};f.event.special.change={filters:{focusout:K,beforedeactivate:K,click:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(c==="radio"||c==="checkbox"||f.nodeName(b,"select"))&&K.call(this,a)},keydown:function(a){var b=a.target,c=f.nodeName(b,"input")?b.type:"";(a.keyCode===13&&!f.nodeName(b,"textarea")||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&K.call(this,a)},beforeactivate:function(a){var b=a.target;f._data(b,"_change_data",J(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in I)f.event.add(this,c+".specialChange",I[c]);return z.test(this.nodeName)},teardown:function(a){f.event.remove(this,".specialChange");return z.test(this.nodeName)}},I=f.event.special.change.filters,I.focus=I.beforeactivate}f.support.focusinBubbles||f.each({focus:"focusin",blur:"focusout"},function(a,b){function e(a){var c=f.event.fix(a);c.type=b,c.originalEvent={},f.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var d=0;f.event.special[b]={setup:function(){d++===0&&c.addEventListener(a,e,!0)},teardown:function(){--d===0&&c.removeEventListener(a,e,!0)}}}),f.each(["bind","one"],function(a,c){f.fn[c]=function(a,d,e){var g;if(typeof a=="object"){for(var h in a)this[c](h,d,a[h],e);return this}if(arguments.length===2||d===!1)e=d,d=b;c==="one"?(g=function(a){f(this).unbind(a,g);return e.apply(this,arguments)},g.guid=e.guid||f.guid++):g=e;if(a==="unload"&&c!=="one")this.one(a,d,e);else for(var i=0,j=this.length;i0?this.bind(b,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d=0,e=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,f,g){f=f||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return f;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(e.call(n)==="[object Array]")if(!u)f.push.apply(f,n);else if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&f.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&f.push(j[t]);else p(n,f);o&&(k(o,h,f,g),k.uniqueSort(f));return f};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=d++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(e.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var f=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g0)for(h=g;h0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(d=0,e=a.length;d-1:f(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=U.test(a)||typeof a!="string"?f(a,b||this.context):0;for(d=0,e=this.length;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a=="string")return f.inArray(this[0],a?f(a):this.parent().children());return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(W(c[0])||W(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling(a.parentNode.firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c),g=T.call(arguments);P.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!V[a]?f.unique(e):e,(this.length>1||R.test(d))&&Q.test(a)&&(e=e.reverse());return this.pushStack(e,a,g.join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var Y=/ jQuery\d+="(?:\d+|null)"/g,Z=/^\s+/,$=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,_=/<([\w:]+)/,ba=/",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
","
"]),f.fn.extend({text:function(a){if(f.isFunction(a))return this.each(function(b){var c=f(this);c.text(a.call(this,b,c.text()))});if(typeof a!="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return f.text(this)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){f(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Y,""):null;if(typeof a=="string"&&!bc.test(a)&&(f.support.leadingWhitespace||!Z.test(a))&&!bg[(_.exec(a)||["",""])[1].toLowerCase()]){a=a.replace($,"<$1>");try{for(var c=0,d=this.length;c1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d=a.cloneNode(!0),e,g,h;if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bj(a,d),e=bk(a),g=bk(d);for(h=0;e[h];++h)bj(e[h],g[h])}if(b){bi(a,d);if(c){e=bk(a),g=bk(d);for(h=0;e[h];++h)bi(e[h],g[h])}}return d},clean:function(a,b,d,e){var g;b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument|| +b[0]&&b[0].ownerDocument||c);var h=[],i;for(var j=0,k;(k=a[j])!=null;j++){typeof k=="number"&&(k+="");if(!k)continue;if(typeof k=="string")if(!bb.test(k))k=b.createTextNode(k);else{k=k.replace($,"<$1>");var l=(_.exec(k)||["",""])[1].toLowerCase(),m=bg[l]||bg._default,n=m[0],o=b.createElement("div");o.innerHTML=m[1]+k+m[2];while(n--)o=o.lastChild;if(!f.support.tbody){var p=ba.test(k),q=l==="table"&&!p?o.firstChild&&o.firstChild.childNodes:m[1]===""&&!p?o.childNodes:[];for(i=q.length-1;i>=0;--i)f.nodeName(q[i],"tbody")&&!q[i].childNodes.length&&q[i].parentNode.removeChild(q[i])}!f.support.leadingWhitespace&&Z.test(k)&&o.insertBefore(b.createTextNode(Z.exec(k)[0]),o.firstChild),k=o.childNodes}var r;if(!f.support.appendChecked)if(k[0]&&typeof (r=k.length)=="number")for(i=0;i=0)return b+"px"}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bp.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle;c.zoom=1;var e=f.isNaN(b)?"":"alpha(opacity="+b*100+")",g=d&&d.filter||c.filter||"";c.filter=bo.test(g)?g.replace(bo,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){var c;f.swap(a,{display:"inline-block"},function(){b?c=bz(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bA=function(a,c){var d,e,g;c=c.replace(br,"-$1").toLowerCase();if(!(e=a.ownerDocument.defaultView))return b;if(g=e.getComputedStyle(a,null))d=g.getPropertyValue(c),d===""&&!f.contains(a.ownerDocument.documentElement,a)&&(d=f.style(a,c));return d}),c.documentElement.currentStyle&&(bB=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bs.test(d)&&bt.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bz=bA||bB,f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)});var bE=/%20/g,bF=/\[\]$/,bG=/\r?\n/g,bH=/#.*$/,bI=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bJ=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bK=/^(?:about|app|app\-storage|.+\-extension|file|widget):$/,bL=/^(?:GET|HEAD)$/,bM=/^\/\//,bN=/\?/,bO=/)<[^<]*)*<\/script>/gi,bP=/^(?:select|textarea)/i,bQ=/\s+/,bR=/([?&])_=[^&]*/,bS=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bT=f.fn.load,bU={},bV={},bW,bX;try{bW=e.href}catch(bY){bW=c.createElement("a"),bW.href="",bW=bW.href}bX=bS.exec(bW.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bT)return bT.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bO,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bP.test(this.nodeName)||bJ.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bG,"\r\n")}}):{name:b.name,value:c.replace(bG,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.bind(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?f.extend(!0,a,f.ajaxSettings,b):(b=a,a=f.extend(!0,f.ajaxSettings,b));for(var c in{context:1,url:1})c in b?a[c]=b[c]:c in f.ajaxSettings&&(a[c]=f.ajaxSettings[c]);return a},ajaxSettings:{url:bW,isLocal:bK.test(bX[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML}},ajaxPrefilter:bZ(bU),ajaxTransport:bZ(bV),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a?4:0;var o,r,u,w=l?ca(d,v,l):b,x,y;if(a>=200&&a<300||a===304){if(d.ifModified){if(x=v.getResponseHeader("Last-Modified"))f.lastModified[k]=x;if(y=v.getResponseHeader("Etag"))f.etag[k]=y}if(a===304)c="notmodified",o=!0;else try{r=cb(d,w),c="success",o=!0}catch(z){c="parsererror",u=z}}else{u=c;if(!c||a)c="error",a<0&&(a=0)}v.status=a,v.statusText=c,o?h.resolveWith(e,[r,c,v]):h.rejectWith(e,[v,c,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.resolveWith(e,[v,c]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f._Deferred(),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bI.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.done,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bH,"").replace(bM,bX[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bQ),d.crossDomain==null&&(r=bS.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bX[1]&&r[2]==bX[2]&&(r[3]||(r[1]==="http:"?80:443))==(bX[3]||(bX[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),b$(bU,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bL.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bN.test(d.url)?"&":"?")+d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bR,"$1_="+x);d.url=y+(y===d.url?(bN.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", */*; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=b$(bV,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){status<2?w(-1,z):f.error(z)}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bE,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=b.contentType==="application/x-www-form-urlencoded"&&typeof b.data=="string";if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n),m.text=h.responseText;try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq,cr=a.webkitRequestAnimationFrame||a.mozRequestAnimationFrame||a.oRequestAnimationFrame;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(cu("show",3),a,b,c);for(var g=0,h=this.length;g=e.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),e.animatedProperties[this.prop]=!0;for(g in e.animatedProperties)e.animatedProperties[g]!==!0&&(c=!1);if(c){e.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){d.style["overflow"+b]=e.overflow[a]}),e.hide&&f(d).hide();if(e.hide||e.show)for(var i in e.animatedProperties)f.style(d,i,e.orig[i]);e.complete.call(d)}return!1}e.duration==Infinity?this.now=b:(h=b-this.startTime,this.state=h/e.duration,this.pos=f.easing[e.animatedProperties[this.prop]](this.state,h,0,1,e.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){for(var a=f.timers,b=0;b
";f.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),d=b.firstChild,e=d.firstChild,h=d.nextSibling.firstChild.firstChild,this.doesNotAddBorder=e.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,e.style.position="fixed",e.style.top="20px",this.supportsFixedPosition=e.offsetTop===20||e.offsetTop===15,e.style.position=e.style.top="",d.style.overflow="hidden",d.style.position="relative",this.subtractsBorderForOverflowNotVisible=e.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),f.offset.initialize=f.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;f.offset.initialize(),f.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(f.css(a,"marginTop"))||0,c+=parseFloat(f.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var d=f.css(a,"position");d==="static"&&(a.style.position="relative");var e=f(a),g=e.offset(),h=f.css(a,"top"),i=f.css(a,"left"),j=(d==="absolute"||d==="fixed")&&f.inArray("auto",[h,i])>-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each(["Left","Top"],function(a,c){var d="scroll"+c;f.fn[d]=function(c){var e,g;if(c===b){e=this[0];if(!e)return null;g=cy(e);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:f.support.boxModel&&g.document.documentElement[d]||g.document.body[d]:e[d]}return this.each(function(){g=cy(this),g?g.scrollTo(a?f(g).scrollLeft():c,a?c:f(g).scrollTop()):this[d]=c})}}),f.each(["Height","Width"],function(a,c){var d=c.toLowerCase();f.fn["inner"+c]=function(){return this[0]?parseFloat(f.css(this[0],d,"padding")):null},f.fn["outer"+c]=function(a){return this[0]?parseFloat(f.css(this[0],d,a?"margin":"border")):null},f.fn[d]=function(a){var e=this[0];if(!e)return a==null?null:this;if(f.isFunction(a))return this.each(function(b){var c=f(this);c[d](a.call(this,b,c[d]()))});if(f.isWindow(e)){var g=e.document.documentElement["client"+c];return e.document.compatMode==="CSS1Compat"&&g||e.document.body["client"+c]||g}if(e.nodeType===9)return Math.max(e.documentElement["client"+c],e.body["scroll"+c],e.documentElement["scroll"+c],e.body["offset"+c],e.documentElement["offset"+c]);if(a===b){var h=f.css(e,d),i=parseFloat(h);return f.isNaN(i)?h:i}return this.css(d,typeof a=="string"?a:a+"px")}}),a.jQuery=a.$=f})(window); \ No newline at end of file diff --git a/src/ciphers/asymmetric/dsa.js b/src/ciphers/asymmetric/dsa.js new file mode 100755 index 00000000..ca477706 --- /dev/null +++ b/src/ciphers/asymmetric/dsa.js @@ -0,0 +1,151 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// A Digital signature algorithm implementation + +function DSA() { + // s1 = ((g**s) mod p) mod q + // s1 = ((s**-1)*(sha-1(m)+(s1*x) mod q) + function sign(hashalgo, m, g, p, q, x) { + // If the output size of the chosen hash is larger than the number of + // bits of q, the hash result is truncated to fit by taking the number + // of leftmost bits equal to the number of bits of q. This (possibly + // truncated) hash function result is treated as a number and used + // directly in the DSA signature algorithm. + var hashed_data = util.getLeftNBits(openpgp_crypto_hashData(hashalgo,m),q.bitLength()); + var hash = new BigInteger(util.hexstrdump(hashed_data), 16); + var k = openpgp_crypto_getRandomBigIntegerInRange(BigInteger.ONE.add(BigInteger.ONE), q.subtract(BigInteger.ONE)); + var s1 = (g.modPow(k,p)).mod(q); + var s2 = (k.modInverse(q).multiply(hash.add(x.multiply(s1)))).mod(q); + var result = new Array(); + result[0] = s1.toMPI(); + result[1] = s2.toMPI(); + return result; + } + function select_hash_algorithm(q) { + var usersetting = openpgp.config.config.prefer_hash_algorithm; + /* + * 1024-bit key, 160-bit q, SHA-1, SHA-224, SHA-256, SHA-384, or SHA-512 hash + * 2048-bit key, 224-bit q, SHA-224, SHA-256, SHA-384, or SHA-512 hash + * 2048-bit key, 256-bit q, SHA-256, SHA-384, or SHA-512 hash + * 3072-bit key, 256-bit q, SHA-256, SHA-384, or SHA-512 hash + */ + switch (Math.round(q.bitLength() / 8)) { + case 20: // 1024 bit + if (usersetting != 2 && + usersetting > 11 && + usersetting != 10 && + usersetting < 8) + return 2; // prefer sha1 + return usersetting; + case 28: // 2048 bit + if (usersetting > 11 && + usersetting < 8) + return 11; + return usersetting; + case 32: // 4096 bit // prefer sha224 + if (usersetting > 10 && + usersetting < 8) + return 8; // prefer sha256 + return usersetting; + default: + util.print_debug("DSA select hash algorithm: returning null for an unknown length of q"); + return null; + + } + } + this.select_hash_algorithm = select_hash_algorithm; + + function verify(hashalgo, s1,s2,m,p,q,g,y) { + var hashed_data = util.getLeftNBits(openpgp_crypto_hashData(hashalgo,m),q.bitLength()); + var hash = new BigInteger(util.hexstrdump(hashed_data), 16); + if (BigInteger.ZERO.compareTo(s1) > 0 || + s1.compareTo(q) > 0 || + BigInteger.ZERO.compareTo(s2) > 0 || + s2.compareTo(q) > 0) { + util.print_error("invalid DSA Signature"); + return null; + } + var w = s2.modInverse(q); + var u1 = hash.multiply(w).mod(q); + var u2 = s1.multiply(w).mod(q); + return g.modPow(u1,p).multiply(y.modPow(u2,p)).mod(p).mod(q); + } + + /* + * unused code. This can be used as a start to write a key generator + * function. + + function generateKey(bitcount) { + var qi = new BigInteger(bitcount, primeCenterie); + var pi = generateP(q, 512); + var gi = generateG(p, q, bitcount); + var xi; + do { + xi = new BigInteger(q.bitCount(), rand); + } while (x.compareTo(BigInteger.ZERO) != 1 && x.compareTo(q) != -1); + var yi = g.modPow(x, p); + return {x: xi, q: qi, p: pi, g: gi, y: yi}; + } + + function generateP(q, bitlength, randomfn) { + if (bitlength % 64 != 0) { + return false; + } + var pTemp; + var pTemp2; + do { + pTemp = randomfn(bitcount, true); + pTemp2 = pTemp.subtract(BigInteger.ONE); + pTemp = pTemp.subtract(pTemp2.remainder(q)); + } while (!pTemp.isProbablePrime(primeCenterie) || pTemp.bitLength() != l); + return pTemp; + } + + function generateG(p, q, bitlength, randomfn) { + var aux = p.subtract(BigInteger.ONE); + var pow = aux.divide(q); + var gTemp; + do { + gTemp = randomfn(bitlength); + } while (gTemp.compareTo(aux) != -1 && gTemp.compareTo(BigInteger.ONE) != 1); + return gTemp.modPow(pow, p); + } + + function generateK(q, bitlength, randomfn) { + var tempK; + do { + tempK = randomfn(bitlength, false); + } while (tempK.compareTo(q) != -1 && tempK.compareTo(BigInteger.ZERO) != 1); + return tempK; + } + + function generateR(q,p) { + k = generateK(q); + var r = g.modPow(k, p).mod(q); + return r; + } + + function generateS(hashfn,k,r,m,q,x) { + var hash = hashfn(m); + s = (k.modInverse(q).multiply(hash.add(x.multiply(r)))).mod(q); + return s; + } */ + this.sign = sign; + this.verify = verify; + // this.generate = generateKey; +} diff --git a/src/ciphers/asymmetric/elgamal.js b/src/ciphers/asymmetric/elgamal.js new file mode 100755 index 00000000..820f0851 --- /dev/null +++ b/src/ciphers/asymmetric/elgamal.js @@ -0,0 +1,48 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// ElGamal implementation + +function Elgamal() { + + function encrypt(m,g,p,y) { + // choose k in {2,...,p-2} + var two = BigInteger.ONE.add(BigInteger.ONE); + var pMinus2 = p.subtract(two); + var k = openpgp_crypto_getRandomBigIntegerInRange(two, pMinus2); + var k = k.mod(pMinus2).add(BigInteger.ONE); + var c = new Array(); + c[0] = g.modPow(k, p); + c[1] = y.modPow(k, p).multiply(m).mod(p).toMPI(); + c[0] = c[0].toMPI(); + return c; + } + + function decrypt(c1,c2,p,x) { + util.print_debug("Elgamal Decrypt:\nc1:"+util.hexstrdump(c1.toMPI())+"\n"+ + "c2:"+util.hexstrdump(c2.toMPI())+"\n"+ + "p:"+util.hexstrdump(p.toMPI())+"\n"+ + "x:"+util.hexstrdump(x.toMPI())); + return (c1.modPow(x, p).modInverse(p)).multiply(c2).mod(p); + //var c = c1.pow(x).modInverse(p); // c0^-a mod p + //return c.multiply(c2).mod(p); + } + + // signing and signature verification using Elgamal is not required by OpenPGP. + this.encrypt = encrypt; + this.decrypt = decrypt; +} \ No newline at end of file diff --git a/src/ciphers/asymmetric/jsbn.js b/src/ciphers/asymmetric/jsbn.js new file mode 100755 index 00000000..7b402783 --- /dev/null +++ b/src/ciphers/asymmetric/jsbn.js @@ -0,0 +1,589 @@ +/* + * Copyright (c) 2003-2005 Tom Wu (tjw@cs.Stanford.EDU) + * All Rights Reserved. + * + * Modified by Recurity Labs GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF + * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * In addition, the following condition applies: + * + * All redistributions must retain an intact copy of this copyright notice + * and disclaimer. + */ + +// Basic JavaScript BN library - subset useful for RSA encryption. + +// Bits per digit +var dbits; + +// JavaScript engine analysis +var canary = 0xdeadbeefcafe; +var j_lm = ((canary&0xffffff)==0xefcafe); + +// (public) Constructor +function BigInteger(a,b,c) { + if(a != null) + if("number" == typeof a) this.fromNumber(a,b,c); + else if(b == null && "string" != typeof a) this.fromString(a,256); + else this.fromString(a,b); +} + +// return new, unset BigInteger +function nbi() { return new BigInteger(null); } + +// am: Compute w_j += (x*this_i), propagate carries, +// c is initial carry, returns final carry. +// c < 3*dvalue, x < 2*dvalue, this_i < dvalue +// We need to select the fastest one that works in this environment. + +// am1: use a single mult and divide to get the high bits, +// max digit bits should be 26 because +// max internal value = 2*dvalue^2-2*dvalue (< 2^53) +function am1(i,x,w,j,c,n) { + while(--n >= 0) { + var v = x*this[i++]+w[j]+c; + c = Math.floor(v/0x4000000); + w[j++] = v&0x3ffffff; + } + return c; +} +// am2 avoids a big mult-and-extract completely. +// Max digit bits should be <= 30 because we do bitwise ops +// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) +function am2(i,x,w,j,c,n) { + var xl = x&0x7fff, xh = x>>15; + while(--n >= 0) { + var l = this[i]&0x7fff; + var h = this[i++]>>15; + var m = xh*l+h*xl; + l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff); + c = (l>>>30)+(m>>>15)+xh*h+(c>>>30); + w[j++] = l&0x3fffffff; + } + return c; +} +// Alternately, set max digit bits to 28 since some +// browsers slow down when dealing with 32-bit numbers. +function am3(i,x,w,j,c,n) { + var xl = x&0x3fff, xh = x>>14; + while(--n >= 0) { + var l = this[i]&0x3fff; + var h = this[i++]>>14; + var m = xh*l+h*xl; + l = xl*l+((m&0x3fff)<<14)+w[j]+c; + c = (l>>28)+(m>>14)+xh*h; + w[j++] = l&0xfffffff; + } + return c; +} +if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) { + BigInteger.prototype.am = am2; + dbits = 30; +} +else if(j_lm && (navigator.appName != "Netscape")) { + BigInteger.prototype.am = am1; + dbits = 26; +} +else { // Mozilla/Netscape seems to prefer am3 + BigInteger.prototype.am = am3; + dbits = 28; +} + +BigInteger.prototype.DB = dbits; +BigInteger.prototype.DM = ((1<= 0; --i) r[i] = this[i]; + r.t = this.t; + r.s = this.s; +} + +// (protected) set from integer value x, -DV <= x < DV +function bnpFromInt(x) { + this.t = 1; + this.s = (x<0)?-1:0; + if(x > 0) this[0] = x; + else if(x < -1) this[0] = x+DV; + else this.t = 0; +} + +// return bigint initialized to value +function nbv(i) { var r = nbi(); r.fromInt(i); return r; } + +// (protected) set from string and radix +function bnpFromString(s,b) { + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 256) k = 8; // byte array + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else { this.fromRadix(s,b); return; } + this.t = 0; + this.s = 0; + var i = s.length, mi = false, sh = 0; + while(--i >= 0) { + var x = (k==8)?s[i]&0xff:intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-") mi = true; + continue; + } + mi = false; + if(sh == 0) + this[this.t++] = x; + else if(sh+k > this.DB) { + this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<>(this.DB-sh)); + } + else + this[this.t-1] |= x<= this.DB) sh -= this.DB; + } + if(k == 8 && (s[0]&0x80) != 0) { + this.s = -1; + if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)< 0 && this[this.t-1] == c) --this.t; +} + +// (public) return string representation in given radix +function bnToString(b) { + if(this.s < 0) return "-"+this.negate().toString(b); + var k; + if(b == 16) k = 4; + else if(b == 8) k = 3; + else if(b == 2) k = 1; + else if(b == 32) k = 5; + else if(b == 4) k = 2; + else return this.toRadix(b); + var km = (1< 0) { + if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); } + while(i >= 0) { + if(p < k) { + d = (this[i]&((1<>(p+=this.DB-k); + } + else { + d = (this[i]>>(p-=k))&km; + if(p <= 0) { p += this.DB; --i; } + } + if(d > 0) m = true; + if(m) r += int2char(d); + } + } + return m?r:"0"; +} + +// (public) -this +function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } + +// (public) |this| +function bnAbs() { return (this.s<0)?this.negate():this; } + +// (public) return + if this > a, - if this < a, 0 if equal +function bnCompareTo(a) { + var r = this.s-a.s; + if(r != 0) return r; + var i = this.t; + r = i-a.t; + if(r != 0) return r; + while(--i >= 0) if((r=this[i]-a[i]) != 0) return r; + return 0; +} + +// returns bit length of the integer x +function nbits(x) { + var r = 1, t; + if((t=x>>>16) != 0) { x = t; r += 16; } + if((t=x>>8) != 0) { x = t; r += 8; } + if((t=x>>4) != 0) { x = t; r += 4; } + if((t=x>>2) != 0) { x = t; r += 2; } + if((t=x>>1) != 0) { x = t; r += 1; } + return r; +} + +// (public) return the number of bits in "this" +function bnBitLength() { + if(this.t <= 0) return 0; + return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM)); +} + +// (protected) r = this << n*DB +function bnpDLShiftTo(n,r) { + var i; + for(i = this.t-1; i >= 0; --i) r[i+n] = this[i]; + for(i = n-1; i >= 0; --i) r[i] = 0; + r.t = this.t+n; + r.s = this.s; +} + +// (protected) r = this >> n*DB +function bnpDRShiftTo(n,r) { + for(var i = n; i < this.t; ++i) r[i-n] = this[i]; + r.t = Math.max(this.t-n,0); + r.s = this.s; +} + +// (protected) r = this << n +function bnpLShiftTo(n,r) { + var bs = n%this.DB; + var cbs = this.DB-bs; + var bm = (1<= 0; --i) { + r[i+ds+1] = (this[i]>>cbs)|c; + c = (this[i]&bm)<= 0; --i) r[i] = 0; + r[ds] = c; + r.t = this.t+ds+1; + r.s = this.s; + r.clamp(); +} + +// (protected) r = this >> n +function bnpRShiftTo(n,r) { + r.s = this.s; + var ds = Math.floor(n/this.DB); + if(ds >= this.t) { r.t = 0; return; } + var bs = n%this.DB; + var cbs = this.DB-bs; + var bm = (1<>bs; + for(var i = ds+1; i < this.t; ++i) { + r[i-ds-1] |= (this[i]&bm)<>bs; + } + if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<>= this.DB; + } + if(a.t < this.t) { + c -= a.s; + while(i < this.t) { + c += this[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c -= a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c -= a.s; + } + r.s = (c<0)?-1:0; + if(c < -1) r[i++] = this.DV+c; + else if(c > 0) r[i++] = c; + r.t = i; + r.clamp(); +} + +// (protected) r = this * a, r != this,a (HAC 14.12) +// "this" should be the larger one if appropriate. +function bnpMultiplyTo(a,r) { + var x = this.abs(), y = a.abs(); + var i = x.t; + r.t = i+y.t; + while(--i >= 0) r[i] = 0; + for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t); + r.s = 0; + r.clamp(); + if(this.s != a.s) BigInteger.ZERO.subTo(r,r); +} + +// (protected) r = this^2, r != this (HAC 14.16) +function bnpSquareTo(r) { + var x = this.abs(); + var i = r.t = 2*x.t; + while(--i >= 0) r[i] = 0; + for(i = 0; i < x.t-1; ++i) { + var c = x.am(i,x[i],r,2*i,0,1); + if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) { + r[i+x.t] -= x.DV; + r[i+x.t+1] = 1; + } + } + if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1); + r.s = 0; + r.clamp(); +} + +// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) +// r != q, this != m. q or r may be null. +function bnpDivRemTo(m,q,r) { + var pm = m.abs(); + if(pm.t <= 0) return; + var pt = this.abs(); + if(pt.t < pm.t) { + if(q != null) q.fromInt(0); + if(r != null) this.copyTo(r); + return; + } + if(r == null) r = nbi(); + var y = nbi(), ts = this.s, ms = m.s; + var nsh = this.DB-nbits(pm[pm.t-1]); // normalize modulus + if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } + else { pm.copyTo(y); pt.copyTo(r); } + var ys = y.t; + var y0 = y[ys-1]; + if(y0 == 0) return; + var yt = y0*(1<1)?y[ys-2]>>this.F2:0); + var d1 = this.FV/yt, d2 = (1<= 0) { + r[r.t++] = 1; + r.subTo(t,r); + } + BigInteger.ONE.dlShiftTo(ys,t); + t.subTo(y,y); // "negative" y so we can replace sub with am later + while(y.t < ys) y[y.t++] = 0; + while(--j >= 0) { + // Estimate quotient digit + var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2); + if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out + y.dlShiftTo(j,t); + r.subTo(t,r); + while(r[i] < --qd) r.subTo(t,r); + } + } + if(q != null) { + r.drShiftTo(ys,q); + if(ts != ms) BigInteger.ZERO.subTo(q,q); + } + r.t = ys; + r.clamp(); + if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder + if(ts < 0) BigInteger.ZERO.subTo(r,r); +} + +// (public) this mod a +function bnMod(a) { + var r = nbi(); + this.abs().divRemTo(a,null,r); + if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); + return r; +} + +// Modular reduction using "classic" algorithm +function Classic(m) { this.m = m; } +function cConvert(x) { + if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); + else return x; +} +function cRevert(x) { return x; } +function cReduce(x) { x.divRemTo(this.m,null,x); } +function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } +function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +Classic.prototype.convert = cConvert; +Classic.prototype.revert = cRevert; +Classic.prototype.reduce = cReduce; +Classic.prototype.mulTo = cMulTo; +Classic.prototype.sqrTo = cSqrTo; + +// (protected) return "-1/this % 2^DB"; useful for Mont. reduction +// justification: +// xy == 1 (mod m) +// xy = 1+km +// xy(2-xy) = (1+km)(1-km) +// x[y(2-xy)] = 1-k^2m^2 +// x[y(2-xy)] == 1 (mod m^2) +// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 +// should reduce x and y(2-xy) by m^2 at each step to keep size bounded. +// JS multiply "overflows" differently from C/C++, so care is needed here. +function bnpInvDigit() { + if(this.t < 1) return 0; + var x = this[0]; + if((x&1) == 0) return 0; + var y = x&3; // y == 1/x mod 2^2 + y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4 + y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8 + y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16 + // last step - calculate inverse mod DV directly; + // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints + y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits + // we really want the negative inverse, and -DV < y < DV + return (y>0)?this.DV-y:-y; +} + +// Montgomery reduction +function Montgomery(m) { + this.m = m; + this.mp = m.invDigit(); + this.mpl = this.mp&0x7fff; + this.mph = this.mp>>15; + this.um = (1<<(m.DB-15))-1; + this.mt2 = 2*m.t; +} + +// xR mod m +function montConvert(x) { + var r = nbi(); + x.abs().dlShiftTo(this.m.t,r); + r.divRemTo(this.m,null,r); + if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); + return r; +} + +// x/R mod m +function montRevert(x) { + var r = nbi(); + x.copyTo(r); + this.reduce(r); + return r; +} + +// x = x/R mod m (HAC 14.32) +function montReduce(x) { + while(x.t <= this.mt2) // pad x so am has enough room later + x[x.t++] = 0; + for(var i = 0; i < this.m.t; ++i) { + // faster way of calculating u0 = x[i]*mp mod DV + var j = x[i]&0x7fff; + var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM; + // use am to combine the multiply-shift-add into one call + j = i+this.m.t; + x[j] += this.m.am(0,u0,x,i,0,this.m.t); + // propagate carry + while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; } + } + x.clamp(); + x.drShiftTo(this.m.t,x); + if(x.compareTo(this.m) >= 0) x.subTo(this.m,x); +} + +// r = "x^2/R mod m"; x != r +function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +// r = "xy/R mod m"; x,y != r +function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + +Montgomery.prototype.convert = montConvert; +Montgomery.prototype.revert = montRevert; +Montgomery.prototype.reduce = montReduce; +Montgomery.prototype.mulTo = montMulTo; +Montgomery.prototype.sqrTo = montSqrTo; + +// (protected) true iff this is even +function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; } + +// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) +function bnpExp(e,z) { + if(e > 0xffffffff || e < 1) return BigInteger.ONE; + var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; + g.copyTo(r); + while(--i >= 0) { + z.sqrTo(r,r2); + if((e&(1< 0) z.mulTo(r2,g,r); + else { var t = r; r = r2; r2 = t; } + } + return z.revert(r); +} + +// (public) this^e % m, 0 <= e < 2^32 +function bnModPowInt(e,m) { + var z; + if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); + return this.exp(e,z); +} + +// protected +BigInteger.prototype.copyTo = bnpCopyTo; +BigInteger.prototype.fromInt = bnpFromInt; +BigInteger.prototype.fromString = bnpFromString; +BigInteger.prototype.clamp = bnpClamp; +BigInteger.prototype.dlShiftTo = bnpDLShiftTo; +BigInteger.prototype.drShiftTo = bnpDRShiftTo; +BigInteger.prototype.lShiftTo = bnpLShiftTo; +BigInteger.prototype.rShiftTo = bnpRShiftTo; +BigInteger.prototype.subTo = bnpSubTo; +BigInteger.prototype.multiplyTo = bnpMultiplyTo; +BigInteger.prototype.squareTo = bnpSquareTo; +BigInteger.prototype.divRemTo = bnpDivRemTo; +BigInteger.prototype.invDigit = bnpInvDigit; +BigInteger.prototype.isEven = bnpIsEven; +BigInteger.prototype.exp = bnpExp; + +// public +BigInteger.prototype.toString = bnToString; +BigInteger.prototype.negate = bnNegate; +BigInteger.prototype.abs = bnAbs; +BigInteger.prototype.compareTo = bnCompareTo; +BigInteger.prototype.bitLength = bnBitLength; +BigInteger.prototype.mod = bnMod; +BigInteger.prototype.modPowInt = bnModPowInt; + +// "constants" +BigInteger.ZERO = nbv(0); +BigInteger.ONE = nbv(1); + diff --git a/src/ciphers/asymmetric/jsbn2.js b/src/ciphers/asymmetric/jsbn2.js new file mode 100755 index 00000000..fb739f50 --- /dev/null +++ b/src/ciphers/asymmetric/jsbn2.js @@ -0,0 +1,714 @@ +/* + * Copyright (c) 2003-2005 Tom Wu (tjw@cs.Stanford.EDU) + * All Rights Reserved. + * + * Modified by Recurity Labs GmbH + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, + * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY + * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + * + * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL, + * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER + * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF + * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT + * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * In addition, the following condition applies: + * + * All redistributions must retain an intact copy of this copyright notice + * and disclaimer. + */ +// Extended JavaScript BN functions, required for RSA private ops. + +// Version 1.1: new BigInteger("0", 10) returns "proper" zero +// Version 1.2: square() API, isProbablePrime fix + +// (public) +function bnClone() { var r = nbi(); this.copyTo(r); return r; } + +// (public) return value as integer +function bnIntValue() { + if(this.s < 0) { + if(this.t == 1) return this[0]-this.DV; + else if(this.t == 0) return -1; + } + else if(this.t == 1) return this[0]; + else if(this.t == 0) return 0; + // assumes 16 < DB < 32 + return ((this[1]&((1<<(32-this.DB))-1))<>24; } + +// (public) return value as short (assumes DB>=16) +function bnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; } + +// (protected) return x s.t. r^x < DV +function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); } + +// (public) 0 if this == 0, 1 if this > 0 +function bnSigNum() { + if(this.s < 0) return -1; + else if(this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0; + else return 1; +} + +// (protected) convert to radix string +function bnpToRadix(b) { + if(b == null) b = 10; + if(this.signum() == 0 || b < 2 || b > 36) return "0"; + var cs = this.chunkSize(b); + var a = Math.pow(b,cs); + var d = nbv(a), y = nbi(), z = nbi(), r = ""; + this.divRemTo(d,y,z); + while(y.signum() > 0) { + r = (a+z.intValue()).toString(b).substr(1) + r; + y.divRemTo(d,y,z); + } + return z.intValue().toString(b) + r; +} + +// (protected) convert from radix string +function bnpFromRadix(s,b) { + this.fromInt(0); + if(b == null) b = 10; + var cs = this.chunkSize(b); + var d = Math.pow(b,cs), mi = false, j = 0, w = 0; + for(var i = 0; i < s.length; ++i) { + var x = intAt(s,i); + if(x < 0) { + if(s.charAt(i) == "-" && this.signum() == 0) mi = true; + continue; + } + w = b*w+x; + if(++j >= cs) { + this.dMultiply(d); + this.dAddOffset(w,0); + j = 0; + w = 0; + } + } + if(j > 0) { + this.dMultiply(Math.pow(b,j)); + this.dAddOffset(w,0); + } + if(mi) BigInteger.ZERO.subTo(this,this); +} + +// (protected) alternate constructor +function bnpFromNumber(a,b,c) { + if("number" == typeof b) { + // new BigInteger(int,int,RNG) + if(a < 2) this.fromInt(1); + else { + this.fromNumber(a,c); + if(!this.testBit(a-1)) // force MSB set + this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this); + if(this.isEven()) this.dAddOffset(1,0); // force odd + while(!this.isProbablePrime(b)) { + this.dAddOffset(2,0); + if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this); + } + } + } + else { + // new BigInteger(int,RNG) + var x = new Array(), t = a&7; + x.length = (a>>3)+1; + b.nextBytes(x); + if(t > 0) x[0] &= ((1< 0) { + if(p < this.DB && (d = this[i]>>p) != (this.s&this.DM)>>p) + r[k++] = d|(this.s<<(this.DB-p)); + while(i >= 0) { + if(p < 8) { + d = (this[i]&((1<>(p+=this.DB-8); + } + else { + d = (this[i]>>(p-=8))&0xff; + if(p <= 0) { p += this.DB; --i; } + } + //if((d&0x80) != 0) d |= -256; + //if(k == 0 && (this.s&0x80) != (d&0x80)) ++k; + if(k > 0 || d != this.s) r[k++] = d; + } + } + return r; +} + +function bnEquals(a) { return(this.compareTo(a)==0); } +function bnMin(a) { return(this.compareTo(a)<0)?this:a; } +function bnMax(a) { return(this.compareTo(a)>0)?this:a; } + +// (protected) r = this op a (bitwise) +function bnpBitwiseTo(a,op,r) { + var i, f, m = Math.min(a.t,this.t); + for(i = 0; i < m; ++i) r[i] = op(this[i],a[i]); + if(a.t < this.t) { + f = a.s&this.DM; + for(i = m; i < this.t; ++i) r[i] = op(this[i],f); + r.t = this.t; + } + else { + f = this.s&this.DM; + for(i = m; i < a.t; ++i) r[i] = op(f,a[i]); + r.t = a.t; + } + r.s = op(this.s,a.s); + r.clamp(); +} + +// (public) this & a +function op_and(x,y) { return x&y; } +function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } + +// (public) this | a +function op_or(x,y) { return x|y; } +function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } + +// (public) this ^ a +function op_xor(x,y) { return x^y; } +function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } + +// (public) this & ~a +function op_andnot(x,y) { return x&~y; } +function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } + +// (public) ~this +function bnNot() { + var r = nbi(); + for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i]; + r.t = this.t; + r.s = ~this.s; + return r; +} + +// (public) this << n +function bnShiftLeft(n) { + var r = nbi(); + if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r); + return r; +} + +// (public) this >> n +function bnShiftRight(n) { + var r = nbi(); + if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r); + return r; +} + +// return index of lowest 1-bit in x, x < 2^31 +function lbit(x) { + if(x == 0) return -1; + var r = 0; + if((x&0xffff) == 0) { x >>= 16; r += 16; } + if((x&0xff) == 0) { x >>= 8; r += 8; } + if((x&0xf) == 0) { x >>= 4; r += 4; } + if((x&3) == 0) { x >>= 2; r += 2; } + if((x&1) == 0) ++r; + return r; +} + +// (public) returns index of lowest 1-bit (or -1 if none) +function bnGetLowestSetBit() { + for(var i = 0; i < this.t; ++i) + if(this[i] != 0) return i*this.DB+lbit(this[i]); + if(this.s < 0) return this.t*this.DB; + return -1; +} + +// return number of 1 bits in x +function cbit(x) { + var r = 0; + while(x != 0) { x &= x-1; ++r; } + return r; +} + +// (public) return number of set bits +function bnBitCount() { + var r = 0, x = this.s&this.DM; + for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x); + return r; +} + +// (public) true iff nth bit is set +function bnTestBit(n) { + var j = Math.floor(n/this.DB); + if(j >= this.t) return(this.s!=0); + return((this[j]&(1<<(n%this.DB)))!=0); +} + +// (protected) this op (1<>= this.DB; + } + if(a.t < this.t) { + c += a.s; + while(i < this.t) { + c += this[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += this.s; + } + else { + c += this.s; + while(i < a.t) { + c += a[i]; + r[i++] = c&this.DM; + c >>= this.DB; + } + c += a.s; + } + r.s = (c<0)?-1:0; + if(c > 0) r[i++] = c; + else if(c < -1) r[i++] = this.DV+c; + r.t = i; + r.clamp(); +} + +// (public) this + a +function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; } + +// (public) this - a +function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; } + +// (public) this * a +function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; } + +// (public) this^2 +function bnSquare() { var r = nbi(); this.squareTo(r); return r; } + +// (public) this / a +function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; } + +// (public) this % a +function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; } + +// (public) [this/a,this%a] +function bnDivideAndRemainder(a) { + var q = nbi(), r = nbi(); + this.divRemTo(a,q,r); + return new Array(q,r); +} + +// (protected) this *= n, this >= 0, 1 < n < DV +function bnpDMultiply(n) { + this[this.t] = this.am(0,n-1,this,0,0,this.t); + ++this.t; + this.clamp(); +} + +// (protected) this += n << w words, this >= 0 +function bnpDAddOffset(n,w) { + if(n == 0) return; + while(this.t <= w) this[this.t++] = 0; + this[w] += n; + while(this[w] >= this.DV) { + this[w] -= this.DV; + if(++w >= this.t) this[this.t++] = 0; + ++this[w]; + } +} + +// A "null" reducer +function NullExp() {} +function nNop(x) { return x; } +function nMulTo(x,y,r) { x.multiplyTo(y,r); } +function nSqrTo(x,r) { x.squareTo(r); } + +NullExp.prototype.convert = nNop; +NullExp.prototype.revert = nNop; +NullExp.prototype.mulTo = nMulTo; +NullExp.prototype.sqrTo = nSqrTo; + +// (public) this^e +function bnPow(e) { return this.exp(e,new NullExp()); } + +// (protected) r = lower n words of "this * a", a.t <= n +// "this" should be the larger one if appropriate. +function bnpMultiplyLowerTo(a,n,r) { + var i = Math.min(this.t+a.t,n); + r.s = 0; // assumes a,this >= 0 + r.t = i; + while(i > 0) r[--i] = 0; + var j; + for(j = r.t-this.t; i < j; ++i) r[i+this.t] = this.am(0,a[i],r,i,0,this.t); + for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a[i],r,i,0,n-i); + r.clamp(); +} + +// (protected) r = "this * a" without lower n words, n > 0 +// "this" should be the larger one if appropriate. +function bnpMultiplyUpperTo(a,n,r) { + --n; + var i = r.t = this.t+a.t-n; + r.s = 0; // assumes a,this >= 0 + while(--i >= 0) r[i] = 0; + for(i = Math.max(n-this.t,0); i < a.t; ++i) + r[this.t+i-n] = this.am(n-i,a[i],r,0,0,this.t+i-n); + r.clamp(); + r.drShiftTo(1,r); +} + +// Barrett modular reduction +function Barrett(m) { + // setup Barrett + this.r2 = nbi(); + this.q3 = nbi(); + BigInteger.ONE.dlShiftTo(2*m.t,this.r2); + this.mu = this.r2.divide(m); + this.m = m; +} + +function barrettConvert(x) { + if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m); + else if(x.compareTo(this.m) < 0) return x; + else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } +} + +function barrettRevert(x) { return x; } + +// x = x mod m (HAC 14.42) +function barrettReduce(x) { + x.drShiftTo(this.m.t-1,this.r2); + if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); } + this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3); + this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2); + while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1); + x.subTo(this.r2,x); + while(x.compareTo(this.m) >= 0) x.subTo(this.m,x); +} + +// r = x^2 mod m; x != r +function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); } + +// r = x*y mod m; x,y != r +function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } + +Barrett.prototype.convert = barrettConvert; +Barrett.prototype.revert = barrettRevert; +Barrett.prototype.reduce = barrettReduce; +Barrett.prototype.mulTo = barrettMulTo; +Barrett.prototype.sqrTo = barrettSqrTo; + +// (public) this^e % m (HAC 14.85) +function bnModPow(e,m) { + var i = e.bitLength(), k, r = nbv(1), z; + if(i <= 0) return r; + else if(i < 18) k = 1; + else if(i < 48) k = 3; + else if(i < 144) k = 4; + else if(i < 768) k = 5; + else k = 6; + if(i < 8) + z = new Classic(m); + else if(m.isEven()) + z = new Barrett(m); + else + z = new Montgomery(m); + + // precomputation + var g = new Array(), n = 3, k1 = k-1, km = (1< 1) { + var g2 = nbi(); + z.sqrTo(g[1],g2); + while(n <= km) { + g[n] = nbi(); + z.mulTo(g2,g[n-2],g[n]); + n += 2; + } + } + + var j = e.t-1, w, is1 = true, r2 = nbi(), t; + i = nbits(e[j])-1; + while(j >= 0) { + if(i >= k1) w = (e[j]>>(i-k1))&km; + else { + w = (e[j]&((1<<(i+1))-1))<<(k1-i); + if(j > 0) w |= e[j-1]>>(this.DB+i-k1); + } + + n = k; + while((w&1) == 0) { w >>= 1; --n; } + if((i -= n) < 0) { i += this.DB; --j; } + if(is1) { // ret == 1, don't bother squaring or multiplying it + g[w].copyTo(r); + is1 = false; + } + else { + while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; } + if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; } + z.mulTo(r2,g[w],r); + } + + while(j >= 0 && (e[j]&(1< 0) { + x.rShiftTo(g,x); + y.rShiftTo(g,y); + } + while(x.signum() > 0) { + if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x); + if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y); + if(x.compareTo(y) >= 0) { + x.subTo(y,x); + x.rShiftTo(1,x); + } + else { + y.subTo(x,y); + y.rShiftTo(1,y); + } + } + if(g > 0) y.lShiftTo(g,y); + return y; +} + +// (protected) this % n, n < 2^26 +function bnpModInt(n) { + if(n <= 0) return 0; + var d = this.DV%n, r = (this.s<0)?n-1:0; + if(this.t > 0) + if(d == 0) r = this[0]%n; + else for(var i = this.t-1; i >= 0; --i) r = (d*r+this[i])%n; + return r; +} + +// (public) 1/this % m (HAC 14.61) +function bnModInverse(m) { + var ac = m.isEven(); + if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; + var u = m.clone(), v = this.clone(); + var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1); + while(u.signum() != 0) { + while(u.isEven()) { + u.rShiftTo(1,u); + if(ac) { + if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); } + a.rShiftTo(1,a); + } + else if(!b.isEven()) b.subTo(m,b); + b.rShiftTo(1,b); + } + while(v.isEven()) { + v.rShiftTo(1,v); + if(ac) { + if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); } + c.rShiftTo(1,c); + } + else if(!d.isEven()) d.subTo(m,d); + d.rShiftTo(1,d); + } + if(u.compareTo(v) >= 0) { + u.subTo(v,u); + if(ac) a.subTo(c,a); + b.subTo(d,b); + } + else { + v.subTo(u,v); + if(ac) c.subTo(a,c); + d.subTo(b,d); + } + } + if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; + if(d.compareTo(m) >= 0) return d.subtract(m); + if(d.signum() < 0) d.addTo(m,d); else return d; + if(d.signum() < 0) return d.add(m); else return d; +} + +var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997]; +var lplim = (1<<26)/lowprimes[lowprimes.length-1]; + +// (public) test primality with certainty >= 1-.5^t +function bnIsProbablePrime(t) { + var i, x = this.abs(); + if(x.t == 1 && x[0] <= lowprimes[lowprimes.length-1]) { + for(i = 0; i < lowprimes.length; ++i) + if(x[0] == lowprimes[i]) return true; + return false; + } + if(x.isEven()) return false; + i = 1; + while(i < lowprimes.length) { + var m = lowprimes[i], j = i+1; + while(j < lowprimes.length && m < lplim) m *= lowprimes[j++]; + m = x.modInt(m); + while(i < j) if(m%lowprimes[i++] == 0) return false; + } + return x.millerRabin(t); +} + +/* added by Recurity Labs */ + +function nbits(x) { + var n = 1, t; + if ((t = x >>> 16) != 0) { + x = t; + n += 16; + } + if ((t = x >> 8) != 0) { + x = t; + n += 8; + } + if ((t = x >> 4) != 0) { + x = t; + n += 4; + } + if ((t = x >> 2) != 0) { + x = t; + n += 2; + } + if ((t = x >> 1) != 0) { + x = t; + n += 1; + } + return n; +} + +function bnToMPI () { + var ba = this.toByteArray(); + var size = (ba.length-1)*8+nbits(ba[0]); + var result = ""; + result += String.fromCharCode((size & 0xFF00) >> 8); + result += String.fromCharCode(size & 0xFF); + result += util.bin2str(ba); + return result; +} +/* END of addition */ + +// (protected) true if probably prime (HAC 4.24, Miller-Rabin) +function bnpMillerRabin(t) { + var n1 = this.subtract(BigInteger.ONE); + var k = n1.getLowestSetBit(); + if(k <= 0) return false; + var r = n1.shiftRight(k); + t = (t+1)>>1; + if(t > lowprimes.length) t = lowprimes.length; + var a = nbi(); + for(var i = 0; i < t; ++i) { + //Pick bases at random, instead of starting at 2 + a.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]); + var y = a.modPow(r,this); + if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { + var j = 1; + while(j++ < k && y.compareTo(n1) != 0) { + y = y.modPowInt(2,this); + if(y.compareTo(BigInteger.ONE) == 0) return false; + } + if(y.compareTo(n1) != 0) return false; + } + } + return true; +} + +// protected +BigInteger.prototype.chunkSize = bnpChunkSize; +BigInteger.prototype.toRadix = bnpToRadix; +BigInteger.prototype.fromRadix = bnpFromRadix; +BigInteger.prototype.fromNumber = bnpFromNumber; +BigInteger.prototype.bitwiseTo = bnpBitwiseTo; +BigInteger.prototype.changeBit = bnpChangeBit; +BigInteger.prototype.addTo = bnpAddTo; +BigInteger.prototype.dMultiply = bnpDMultiply; +BigInteger.prototype.dAddOffset = bnpDAddOffset; +BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; +BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; +BigInteger.prototype.modInt = bnpModInt; +BigInteger.prototype.millerRabin = bnpMillerRabin; + +// public +BigInteger.prototype.clone = bnClone; +BigInteger.prototype.intValue = bnIntValue; +BigInteger.prototype.byteValue = bnByteValue; +BigInteger.prototype.shortValue = bnShortValue; +BigInteger.prototype.signum = bnSigNum; +BigInteger.prototype.toByteArray = bnToByteArray; +BigInteger.prototype.equals = bnEquals; +BigInteger.prototype.min = bnMin; +BigInteger.prototype.max = bnMax; +BigInteger.prototype.and = bnAnd; +BigInteger.prototype.or = bnOr; +BigInteger.prototype.xor = bnXor; +BigInteger.prototype.andNot = bnAndNot; +BigInteger.prototype.not = bnNot; +BigInteger.prototype.shiftLeft = bnShiftLeft; +BigInteger.prototype.shiftRight = bnShiftRight; +BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; +BigInteger.prototype.bitCount = bnBitCount; +BigInteger.prototype.testBit = bnTestBit; +BigInteger.prototype.setBit = bnSetBit; +BigInteger.prototype.clearBit = bnClearBit; +BigInteger.prototype.flipBit = bnFlipBit; +BigInteger.prototype.add = bnAdd; +BigInteger.prototype.subtract = bnSubtract; +BigInteger.prototype.multiply = bnMultiply; +BigInteger.prototype.divide = bnDivide; +BigInteger.prototype.remainder = bnRemainder; +BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; +BigInteger.prototype.modPow = bnModPow; +BigInteger.prototype.modInverse = bnModInverse; +BigInteger.prototype.pow = bnPow; +BigInteger.prototype.gcd = bnGCD; +BigInteger.prototype.isProbablePrime = bnIsProbablePrime; +BigInteger.prototype.toMPI = bnToMPI; + +// JSBN-specific extension +BigInteger.prototype.square = bnSquare; diff --git a/src/ciphers/asymmetric/rsa.js b/src/ciphers/asymmetric/rsa.js new file mode 100755 index 00000000..8f88c0f4 --- /dev/null +++ b/src/ciphers/asymmetric/rsa.js @@ -0,0 +1,75 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// +// RSA implementation + +function RSA() { + /** + * This function uses jsbn Big Num library to decrypt RSA + * @param m + * message + * @param d + * RSA d as BigInteger + * @param p + * RSA p as BigInteger + * @param q + * RSA q as BigInteger + * @param u + * RSA u as BigInteger + * @return + */ + function decrypt(m, d, p, q, u) { + var xp = m.mod(p).modPow(d.mod(p.subtract(BigInteger.ONE)), p); + var xq = m.mod(q).modPow(d.mod(q.subtract(BigInteger.ONE)), q); + util.print_debug("rsa.js decrypt\nxpn:"+util.hexstrdump(xp.toMPI())+"\nxqn:"+util.hexstrdump(xq.toMPI())); + + var t = xq.subtract(xp); + if (t[0] == 0) { + t = xp.subtract(xq); + t = t.multiply(u).mod(q); + t = q.subtract(t); + } else { + t = t.multiply(u).mod(q); + } + return t.multiply(p).add(xp); + } + + /** + * encrypt message + * @param m message as BigInteger + * @param e public MPI part as BigInteger + * @param n public MPI part as BigInteger + * @return BigInteger + */ + function encrypt(m,e,n) { + return m.modPowInt(e, n); + } + + /* Sign and Verify */ + function sign(m,d,n) { + return m.modPow(d, n); + } + + function verify(x,e,n) { + return x.modPowInt(e, n); + } + + this.encrypt = encrypt; + this.decrypt = decrypt; + this.verify = verify; + this.sign = sign; +} \ No newline at end of file diff --git a/src/ciphers/hash/md5.js b/src/ciphers/hash/md5.js new file mode 100755 index 00000000..a913b098 --- /dev/null +++ b/src/ciphers/hash/md5.js @@ -0,0 +1,374 @@ +/* + * md5.jvs 1.0b 27/06/96 + * + * Javascript implementation of the RSA Data Security, Inc. MD5 + * Message-Digest Algorithm. + * + * Copyright (c) 1996 Henri Torgemane. All Rights Reserved. + * + * Modified by Recurity Labs GmbH (http://www.recurity-labs.com) + * + * Permission to use, copy, modify, and distribute this software + * and its documentation for any purposes and without + * fee is hereby granted provided that this copyright notice + * appears in all copies. + * + * Of course, this soft is provided "as is" without express or implied + * warranty of any kind. + */ + + +function MD5(entree) { + +function array(n) { + for(i=0;i> 4 with it.. + * Of course, these functions are slower than the original would be, but + * at least, they work! + */ + +function integer(n) { return n%(0xffffffff+1); } + +function shr(a,b) { + a=integer(a); + b=integer(b); + if (a-0x80000000>=0) { + a=a%0x80000000; + a>>=b; + a+=0x40000000>>(b-1); + } else + a>>=b; + return a; +} + +function shl1(a) { + a=a%0x80000000; + if (a&0x40000000==0x40000000) + { + a-=0x40000000; + a*=2; + a+=0x80000000; + } else + a*=2; + return a; +} + +function shl(a,b) { + a=integer(a); + b=integer(b); + for (var i=0;i=0) + if (t2>=0) + return ((t1&t2)+0x80000000); + else + return (t1&b); + else + if (t2>=0) + return (a&t2); + else + return (a&b); +} + +function or(a,b) { + a=integer(a); + b=integer(b); + var t1=(a-0x80000000); + var t2=(b-0x80000000); + if (t1>=0) + if (t2>=0) + return ((t1|t2)+0x80000000); + else + return ((t1|b)+0x80000000); + else + if (t2>=0) + return ((a|t2)+0x80000000); + else + return (a|b); +} + +function xor(a,b) { + a=integer(a); + b=integer(b); + var t1=(a-0x80000000); + var t2=(b-0x80000000); + if (t1>=0) + if (t2>=0) + return (t1^t2); + else + return ((t1^b)+0x80000000); + else + if (t2>=0) + return ((a^t2)+0x80000000); + else + return (a^b); +} + +function not(a) { + a=integer(a); + return (0xffffffff-a); +} + +/* Here begin the real algorithm */ + + var state = new array(4); + var count = new array(2); + count[0] = 0; + count[1] = 0; + var buffer = new array(64); + var transformBuffer = new array(16); + var digestBits = new array(16); + + var S11 = 7; + var S12 = 12; + var S13 = 17; + var S14 = 22; + var S21 = 5; + var S22 = 9; + var S23 = 14; + var S24 = 20; + var S31 = 4; + var S32 = 11; + var S33 = 16; + var S34 = 23; + var S41 = 6; + var S42 = 10; + var S43 = 15; + var S44 = 21; + + function F(x,y,z) { + return or(and(x,y),and(not(x),z)); + } + + function G(x,y,z) { + return or(and(x,z),and(y,not(z))); + } + + function H(x,y,z) { + return xor(xor(x,y),z); + } + + function I(x,y,z) { + return xor(y ,or(x , not(z))); + } + + function rotateLeft(a,n) { + return or(shl(a, n),(shr(a,(32 - n)))); + } + + function FF(a,b,c,d,x,s,ac) { + a = a+F(b, c, d) + x + ac; + a = rotateLeft(a, s); + a = a+b; + return a; + } + + function GG(a,b,c,d,x,s,ac) { + a = a+G(b, c, d) +x + ac; + a = rotateLeft(a, s); + a = a+b; + return a; + } + + function HH(a,b,c,d,x,s,ac) { + a = a+H(b, c, d) + x + ac; + a = rotateLeft(a, s); + a = a+b; + return a; + } + + function II(a,b,c,d,x,s,ac) { + a = a+I(b, c, d) + x + ac; + a = rotateLeft(a, s); + a = a+b; + return a; + } + + function transform(buf,offset) { + var a=0, b=0, c=0, d=0; + var x = transformBuffer; + + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + + for (i = 0; i < 16; i++) { + x[i] = and(buf[i*4+offset],0xff); + for (j = 1; j < 4; j++) { + x[i]+=shl(and(buf[i*4+j+offset] ,0xff), j * 8); + } + } + + /* Round 1 */ + a = FF ( a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + d = FF ( d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + c = FF ( c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + b = FF ( b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + a = FF ( a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + d = FF ( d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + c = FF ( c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + b = FF ( b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + a = FF ( a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + d = FF ( d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + c = FF ( c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + b = FF ( b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + a = FF ( a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + d = FF ( d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + c = FF ( c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + b = FF ( b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + a = GG ( a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + d = GG ( d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + c = GG ( c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + b = GG ( b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + a = GG ( a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + d = GG ( d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + c = GG ( c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + b = GG ( b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + a = GG ( a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + d = GG ( d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + c = GG ( c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + b = GG ( b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + a = GG ( a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + d = GG ( d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + c = GG ( c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + b = GG ( b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + a = HH ( a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + d = HH ( d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + c = HH ( c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + b = HH ( b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + a = HH ( a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + d = HH ( d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + c = HH ( c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + b = HH ( b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + a = HH ( a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + d = HH ( d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + c = HH ( c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + b = HH ( b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + a = HH ( a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + d = HH ( d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + c = HH ( c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + b = HH ( b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + a = II ( a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + d = II ( d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + c = II ( c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + b = II ( b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + a = II ( a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + d = II ( d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + c = II ( c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + b = II ( b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + a = II ( a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + d = II ( d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + c = II ( c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + b = II ( b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + a = II ( a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + d = II ( d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + c = II ( c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + b = II ( b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] +=a; + state[1] +=b; + state[2] +=c; + state[3] +=d; + + } + + function init() { + count[0]=count[1] = 0; + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; + for (i = 0; i < digestBits.length; i++) + digestBits[i] = 0; + } + + function update(b) { + var index,i; + + index = and(shr(count[0],3) , 0x3f); + if (count[0]<0xffffffff-7) + count[0] += 8; + else { + count[1]++; + count[0]-=0xffffffff+1; + count[0]+=8; + } + buffer[index] = and(b,0xff); + if (index >= 63) { + transform(buffer, 0); + } + } + + function finish() { + var bits = new array(8); + var padding; + var i=0, index=0, padLen=0; + + for (i = 0; i < 4; i++) { + bits[i] = and(shr(count[0],(i * 8)), 0xff); + } + for (i = 0; i < 4; i++) { + bits[i+4]=and(shr(count[1],(i * 8)), 0xff); + } + index = and(shr(count[0], 3) ,0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + padding = new array(64); + padding[0] = 0x80; + for (i=0;i>5] >>> (32 - 8 - i%32)) & mask); +return str; +} + +// modified by Recurity Labs GmbH + var l,s,k,ka,kb,kc,kd; + + init(); + for (k=0;k>> (32 - n))); +} + +function F(x, y, z) +{ + return new Number(x ^ y ^ z); +} + +function G(x, y, z) +{ + return new Number((x & y) | (~x & z)); +} + +function H(x, y, z) +{ + return new Number((x | ~y) ^ z); +} + +function I(x, y, z) +{ + return new Number((x & z) | (y & ~z)); +} + +function J(x, y, z) +{ + return new Number(x ^ (y | ~z)); +} + +function mixOneRound(a, b, c, d, e, x, s, roundNumber) +{ + switch (roundNumber) + { + case 0 : a += F(b, c, d) + x + 0x00000000; break; + case 1 : a += G(b, c, d) + x + 0x5a827999; break; + case 2 : a += H(b, c, d) + x + 0x6ed9eba1; break; + case 3 : a += I(b, c, d) + x + 0x8f1bbcdc; break; + case 4 : a += J(b, c, d) + x + 0xa953fd4e; break; + case 5 : a += J(b, c, d) + x + 0x50a28be6; break; + case 6 : a += I(b, c, d) + x + 0x5c4dd124; break; + case 7 : a += H(b, c, d) + x + 0x6d703ef3; break; + case 8 : a += G(b, c, d) + x + 0x7a6d76e9; break; + case 9 : a += F(b, c, d) + x + 0x00000000; break; + + default : document.write("Bogus round number"); break; + } + + a = ROL(a, s) + e; + c = ROL(c, 10); + + a &= 0xffffffff; + b &= 0xffffffff; + c &= 0xffffffff; + d &= 0xffffffff; + e &= 0xffffffff; + + var retBlock = new Array(); + retBlock[0] = a; + retBlock[1] = b; + retBlock[2] = c; + retBlock[3] = d; + retBlock[4] = e; + retBlock[5] = x; + retBlock[6] = s; + + return retBlock; +} + +function MDinit (MDbuf) +{ + MDbuf[0] = 0x67452301; + MDbuf[1] = 0xefcdab89; + MDbuf[2] = 0x98badcfe; + MDbuf[3] = 0x10325476; + MDbuf[4] = 0xc3d2e1f0; +} + +var ROLs = [ + [11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8], + [ 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12], + [11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5], + [11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12], + [ 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6], + [ 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6], + [ 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11], + [ 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5], + [15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8], + [ 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11] +]; + +var indexes = [ + [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [ 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8], + [ 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12], + [ 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2], + [ 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13], + [ 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12], + [ 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2], + [15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13], + [ 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14], + [12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11] +]; + +function compress (MDbuf, X) +{ + blockA = new Array(); + blockB = new Array(); + + var retBlock; + + for (var i=0; i < 5; i++) + { + blockA[i] = new Number(MDbuf[i]); + blockB[i] = new Number(MDbuf[i]); + } + + var step = 0; + for (var j = 0; j < 5; j++) + { + for (var i = 0; i < 16; i++) + { + retBlock = mixOneRound( + blockA[(step+0) % 5], + blockA[(step+1) % 5], + blockA[(step+2) % 5], + blockA[(step+3) % 5], + blockA[(step+4) % 5], + X[indexes[j][i]], + ROLs[j][i], + j + ); + + blockA[(step+0) % 5] = retBlock[0]; + blockA[(step+1) % 5] = retBlock[1]; + blockA[(step+2) % 5] = retBlock[2]; + blockA[(step+3) % 5] = retBlock[3]; + blockA[(step+4) % 5] = retBlock[4]; + + step += 4; + } + } + + step = 0; + for (var j = 5; j < 10; j++) + { + for (var i = 0; i < 16; i++) + { + retBlock = mixOneRound( + blockB[(step+0) % 5], + blockB[(step+1) % 5], + blockB[(step+2) % 5], + blockB[(step+3) % 5], + blockB[(step+4) % 5], + X[indexes[j][i]], + ROLs[j][i], + j + ); + + blockB[(step+0) % 5] = retBlock[0]; + blockB[(step+1) % 5] = retBlock[1]; + blockB[(step+2) % 5] = retBlock[2]; + blockB[(step+3) % 5] = retBlock[3]; + blockB[(step+4) % 5] = retBlock[4]; + + step += 4; + } + } + + blockB[3] += blockA[2] + MDbuf[1]; + MDbuf[1] = MDbuf[2] + blockA[3] + blockB[4]; + MDbuf[2] = MDbuf[3] + blockA[4] + blockB[0]; + MDbuf[3] = MDbuf[4] + blockA[0] + blockB[1]; + MDbuf[4] = MDbuf[0] + blockA[1] + blockB[2]; + MDbuf[0] = blockB[3]; +} + +function zeroX(X) +{ + for (var i = 0; i < 16; i++) { X[i] = 0; } +} + +function MDfinish (MDbuf, strptr, lswlen, mswlen) +{ + var X = new Array(16); + zeroX(X); + + var j = 0; + for (var i=0; i < (lswlen & 63); i++) + { + X[i >>> 2] ^= (strptr.charCodeAt(j++) & 255) << (8 * (i & 3)); + } + + X[(lswlen >>> 2) & 15] ^= 1 << (8 * (lswlen & 3) + 7); + + if ((lswlen & 63) > 55) + { + compress(MDbuf, X); + var X = new Array(16); + zeroX(X); + } + + X[14] = lswlen << 3; + X[15] = (lswlen >>> 29) | (mswlen << 3); + + compress(MDbuf, X); +} + +function BYTES_TO_DWORD(fourChars) +{ + var tmp = (fourChars.charCodeAt(3) & 255) << 24; + tmp |= (fourChars.charCodeAt(2) & 255) << 16; + tmp |= (fourChars.charCodeAt(1) & 255) << 8; + tmp |= (fourChars.charCodeAt(0) & 255); + + return tmp; +} + +function RMD(message) +{ + var MDbuf = new Array(RMDsize / 32); + var hashcode = new Array(RMDsize / 8); + var length; + var nbytes; + + MDinit(MDbuf); + length = message.length; + + var X = new Array(16); + zeroX(X); + + var j=0; + for (var nbytes=length; nbytes > 63; nbytes -= 64) + { + for (var i=0; i < 16; i++) + { + X[i] = BYTES_TO_DWORD(message.substr(j, 4)); + j += 4; + } + compress(MDbuf, X); + } + + MDfinish(MDbuf, message.substr(j), length, 0); + + for (var i=0; i < RMDsize / 8; i += 4) + { + hashcode[i] = MDbuf[i >>> 2] & 255; + hashcode[i+1] = (MDbuf[i >>> 2] >>> 8) & 255; + hashcode[i+2] = (MDbuf[i >>> 2] >>> 16) & 255; + hashcode[i+3] = (MDbuf[i >>> 2] >>> 24) & 255; + } + + return hashcode; +} + + +function RMDstring(message) +{ + var hashcode = RMD(message); + var retString = ""; + + for (var i=0; i < RMDsize/8; i++) + { + retString += String.fromCharCode(hashcode[i]); + } + + return retString; +} \ No newline at end of file diff --git a/src/ciphers/hash/sha.js b/src/ciphers/hash/sha.js new file mode 100755 index 00000000..fd37fbb8 --- /dev/null +++ b/src/ciphers/hash/sha.js @@ -0,0 +1,1221 @@ +/* A JavaScript implementation of the SHA family of hashes, as defined in FIPS + * PUB 180-2 as well as the corresponding HMAC implementation as defined in + * FIPS PUB 198a + * + * Version 1.3 Copyright Brian Turek 2008-2010 + * Distributed under the BSD License + * See http://jssha.sourceforge.net/ for more information + * + * Several functions taken from Paul Johnson + */ + +/* Modified by Recurity Labs GmbH + * + * This code has been slightly modified direct string output: + * - bin2bstr has been added + * - following wrappers of this library have been added: + * - str_sha1 + * - str_sha256 + * - str_sha224 + * - str_sha384 + * - str_sha512 + */ + +(function () +{ + /* + * Configurable variables. Defaults typically work + */ + /* Number of Bits Per character (8 for ASCII, 16 for Unicode) */ + var charSize = 8, + /* base-64 pad character. "=" for strict RFC compliance */ + b64pad = "", + /* hex output format. 0 - lowercase; 1 - uppercase */ + hexCase = 0, + + /* + * Int_64 is a object for 2 32-bit numbers emulating a 64-bit number + * + * @constructor + * @param {Number} msint_32 The most significant 32-bits of a 64-bit number + * @param {Number} lsint_32 The least significant 32-bits of a 64-bit number + */ + Int_64 = function (msint_32, lsint_32) + { + this.highOrder = msint_32; + this.lowOrder = lsint_32; + }, + + /* + * Convert a string to an array of big-endian words + * If charSize is ASCII, characters >255 have their hi-byte silently + * ignored. + * + * @param {String} str String to be converted to binary representation + * @return Integer array representation of the parameter + */ + str2binb = function (str) + { + var bin = [], mask = (1 << charSize) - 1, + length = str.length * charSize, i; + + for (i = 0; i < length; i += charSize) + { + bin[i >> 5] |= (str.charCodeAt(i / charSize) & mask) << + (32 - charSize - (i % 32)); + } + + return bin; + }, + + /* + * Convert a hex string to an array of big-endian words + * + * @param {String} str String to be converted to binary representation + * @return Integer array representation of the parameter + */ + hex2binb = function (str) + { + var bin = [], length = str.length, i, num; + + for (i = 0; i < length; i += 2) + { + num = parseInt(str.substr(i, 2), 16); + if (!isNaN(num)) + { + bin[i >> 3] |= num << (24 - (4 * (i % 8))); + } + else + { + return "INVALID HEX STRING"; + } + } + + return bin; + }, + + /* + * Convert an array of big-endian words to a hex string. + * + * @private + * @param {Array} binarray Array of integers to be converted to hexidecimal + * representation + * @return Hexidecimal representation of the parameter in String form + */ + binb2hex = function (binarray) + { + var hex_tab = (hexCase) ? "0123456789ABCDEF" : "0123456789abcdef", + str = "", length = binarray.length * 4, i, srcByte; + + for (i = 0; i < length; i += 1) + { + srcByte = binarray[i >> 2] >> ((3 - (i % 4)) * 8); + str += hex_tab.charAt((srcByte >> 4) & 0xF) + + hex_tab.charAt(srcByte & 0xF); + } + + return str; + }, + + /* + * Convert an array of big-endian words to a base-64 string + * + * @private + * @param {Array} binarray Array of integers to be converted to base-64 + * representation + * @return Base-64 encoded representation of the parameter in String form + */ + binb2b64 = function (binarray) + { + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + + "0123456789+/", str = "", length = binarray.length * 4, i, j, + triplet; + + for (i = 0; i < length; i += 3) + { + triplet = (((binarray[i >> 2] >> 8 * (3 - i % 4)) & 0xFF) << 16) | + (((binarray[i + 1 >> 2] >> 8 * (3 - (i + 1) % 4)) & 0xFF) << 8) | + ((binarray[i + 2 >> 2] >> 8 * (3 - (i + 2) % 4)) & 0xFF); + for (j = 0; j < 4; j += 1) + { + if (i * 8 + j * 6 <= binarray.length * 32) + { + str += tab.charAt((triplet >> 6 * (3 - j)) & 0x3F); + } + else + { + str += b64pad; + } + } + } + return str; + }, + + /* + * Convert an array of big-endian words to a string + */ + binb2str = function (bin) + { + var str = ""; + var mask = (1 << 8) - 1; + for(var i = 0; i < bin.length * 32; i += 8) + str += String.fromCharCode((bin[i>>5] >>> (24 - i%32)) & mask); + return str; + }, + /* + * The 32-bit implementation of circular rotate left + * + * @private + * @param {Number} x The 32-bit integer argument + * @param {Number} n The number of bits to shift + * @return The x shifted circularly by n bits + */ + rotl_32 = function (x, n) + { + return (x << n) | (x >>> (32 - n)); + }, + + /* + * The 32-bit implementation of circular rotate right + * + * @private + * @param {Number} x The 32-bit integer argument + * @param {Number} n The number of bits to shift + * @return The x shifted circularly by n bits + */ + rotr_32 = function (x, n) + { + return (x >>> n) | (x << (32 - n)); + }, + + /* + * The 64-bit implementation of circular rotate right + * + * @private + * @param {Int_64} x The 64-bit integer argument + * @param {Number} n The number of bits to shift + * @return The x shifted circularly by n bits + */ + rotr_64 = function (x, n) + { + if (n <= 32) + { + return new Int_64( + (x.highOrder >>> n) | (x.lowOrder << (32 - n)), + (x.lowOrder >>> n) | (x.highOrder << (32 - n)) + ); + } + else + { + return new Int_64( + (x.lowOrder >>> n) | (x.highOrder << (32 - n)), + (x.highOrder >>> n) | (x.lowOrder << (32 - n)) + ); + } + }, + + /* + * The 32-bit implementation of shift right + * + * @private + * @param {Number} x The 32-bit integer argument + * @param {Number} n The number of bits to shift + * @return The x shifted by n bits + */ + shr_32 = function (x, n) + { + return x >>> n; + }, + + /* + * The 64-bit implementation of shift right + * + * @private + * @param {Int_64} x The 64-bit integer argument + * @param {Number} n The number of bits to shift + * @return The x shifted by n bits + */ + shr_64 = function (x, n) + { + if (n <= 32) + { + return new Int_64( + x.highOrder >>> n, + x.lowOrder >>> n | (x.highOrder << (32 - n)) + ); + } + else + { + return new Int_64( + 0, + x.highOrder << (32 - n) + ); + } + }, + + /* + * The 32-bit implementation of the NIST specified Parity function + * + * @private + * @param {Number} x The first 32-bit integer argument + * @param {Number} y The second 32-bit integer argument + * @param {Number} z The third 32-bit integer argument + * @return The NIST specified output of the function + */ + parity_32 = function (x, y, z) + { + return x ^ y ^ z; + }, + + /* + * The 32-bit implementation of the NIST specified Ch function + * + * @private + * @param {Number} x The first 32-bit integer argument + * @param {Number} y The second 32-bit integer argument + * @param {Number} z The third 32-bit integer argument + * @return The NIST specified output of the function + */ + ch_32 = function (x, y, z) + { + return (x & y) ^ (~x & z); + }, + + /* + * The 64-bit implementation of the NIST specified Ch function + * + * @private + * @param {Int_64} x The first 64-bit integer argument + * @param {Int_64} y The second 64-bit integer argument + * @param {Int_64} z The third 64-bit integer argument + * @return The NIST specified output of the function + */ + ch_64 = function (x, y, z) + { + return new Int_64( + (x.highOrder & y.highOrder) ^ (~x.highOrder & z.highOrder), + (x.lowOrder & y.lowOrder) ^ (~x.lowOrder & z.lowOrder) + ); + }, + + /* + * The 32-bit implementation of the NIST specified Maj function + * + * @private + * @param {Number} x The first 32-bit integer argument + * @param {Number} y The second 32-bit integer argument + * @param {Number} z The third 32-bit integer argument + * @return The NIST specified output of the function + */ + maj_32 = function (x, y, z) + { + return (x & y) ^ (x & z) ^ (y & z); + }, + + /* + * The 64-bit implementation of the NIST specified Maj function + * + * @private + * @param {Int_64} x The first 64-bit integer argument + * @param {Int_64} y The second 64-bit integer argument + * @param {Int_64} z The third 64-bit integer argument + * @return The NIST specified output of the function + */ + maj_64 = function (x, y, z) + { + return new Int_64( + (x.highOrder & y.highOrder) ^ + (x.highOrder & z.highOrder) ^ + (y.highOrder & z.highOrder), + (x.lowOrder & y.lowOrder) ^ + (x.lowOrder & z.lowOrder) ^ + (y.lowOrder & z.lowOrder) + ); + }, + + /* + * The 32-bit implementation of the NIST specified Sigma0 function + * + * @private + * @param {Number} x The 32-bit integer argument + * @return The NIST specified output of the function + */ + sigma0_32 = function (x) + { + return rotr_32(x, 2) ^ rotr_32(x, 13) ^ rotr_32(x, 22); + }, + + /* + * The 64-bit implementation of the NIST specified Sigma0 function + * + * @private + * @param {Int_64} x The 64-bit integer argument + * @return The NIST specified output of the function + */ + sigma0_64 = function (x) + { + var rotr28 = rotr_64(x, 28), rotr34 = rotr_64(x, 34), + rotr39 = rotr_64(x, 39); + + return new Int_64( + rotr28.highOrder ^ rotr34.highOrder ^ rotr39.highOrder, + rotr28.lowOrder ^ rotr34.lowOrder ^ rotr39.lowOrder); + }, + + /* + * The 32-bit implementation of the NIST specified Sigma1 function + * + * @private + * @param {Number} x The 32-bit integer argument + * @return The NIST specified output of the function + */ + sigma1_32 = function (x) + { + return rotr_32(x, 6) ^ rotr_32(x, 11) ^ rotr_32(x, 25); + }, + + /* + * The 64-bit implementation of the NIST specified Sigma1 function + * + * @private + * @param {Int_64} x The 64-bit integer argument + * @return The NIST specified output of the function + */ + sigma1_64 = function (x) + { + var rotr14 = rotr_64(x, 14), rotr18 = rotr_64(x, 18), + rotr41 = rotr_64(x, 41); + + return new Int_64( + rotr14.highOrder ^ rotr18.highOrder ^ rotr41.highOrder, + rotr14.lowOrder ^ rotr18.lowOrder ^ rotr41.lowOrder); + }, + + /* + * The 32-bit implementation of the NIST specified Gamma0 function + * + * @private + * @param {Number} x The 32-bit integer argument + * @return The NIST specified output of the function + */ + gamma0_32 = function (x) + { + return rotr_32(x, 7) ^ rotr_32(x, 18) ^ shr_32(x, 3); + }, + + /* + * The 64-bit implementation of the NIST specified Gamma0 function + * + * @private + * @param {Int_64} x The 64-bit integer argument + * @return The NIST specified output of the function + */ + gamma0_64 = function (x) + { + var rotr1 = rotr_64(x, 1), rotr8 = rotr_64(x, 8), shr7 = shr_64(x, 7); + + return new Int_64( + rotr1.highOrder ^ rotr8.highOrder ^ shr7.highOrder, + rotr1.lowOrder ^ rotr8.lowOrder ^ shr7.lowOrder + ); + }, + + /* + * The 32-bit implementation of the NIST specified Gamma1 function + * + * @private + * @param {Number} x The 32-bit integer argument + * @return The NIST specified output of the function + */ + gamma1_32 = function (x) + { + return rotr_32(x, 17) ^ rotr_32(x, 19) ^ shr_32(x, 10); + }, + + /* + * The 64-bit implementation of the NIST specified Gamma1 function + * + * @private + * @param {Int_64} x The 64-bit integer argument + * @return The NIST specified output of the function + */ + gamma1_64 = function (x) + { + var rotr19 = rotr_64(x, 19), rotr61 = rotr_64(x, 61), + shr6 = shr_64(x, 6); + + return new Int_64( + rotr19.highOrder ^ rotr61.highOrder ^ shr6.highOrder, + rotr19.lowOrder ^ rotr61.lowOrder ^ shr6.lowOrder + ); + }, + + /* + * Add two 32-bit integers, wrapping at 2^32. This uses 16-bit operations + * internally to work around bugs in some JS interpreters. + * + * @private + * @param {Number} x The first 32-bit integer argument to be added + * @param {Number} y The second 32-bit integer argument to be added + * @return The sum of x + y + */ + safeAdd_32_2 = function (x, y) + { + var lsw = (x & 0xFFFF) + (y & 0xFFFF), + msw = (x >>> 16) + (y >>> 16) + (lsw >>> 16); + + return ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + }, + + /* + * Add four 32-bit integers, wrapping at 2^32. This uses 16-bit operations + * internally to work around bugs in some JS interpreters. + * + * @private + * @param {Number} a The first 32-bit integer argument to be added + * @param {Number} b The second 32-bit integer argument to be added + * @param {Number} c The third 32-bit integer argument to be added + * @param {Number} d The fourth 32-bit integer argument to be added + * @return The sum of a + b + c + d + */ + safeAdd_32_4 = function (a, b, c, d) + { + var lsw = (a & 0xFFFF) + (b & 0xFFFF) + (c & 0xFFFF) + (d & 0xFFFF), + msw = (a >>> 16) + (b >>> 16) + (c >>> 16) + (d >>> 16) + + (lsw >>> 16); + + return ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + }, + + /* + * Add five 32-bit integers, wrapping at 2^32. This uses 16-bit operations + * internally to work around bugs in some JS interpreters. + * + * @private + * @param {Number} a The first 32-bit integer argument to be added + * @param {Number} b The second 32-bit integer argument to be added + * @param {Number} c The third 32-bit integer argument to be added + * @param {Number} d The fourth 32-bit integer argument to be added + * @param {Number} e The fifth 32-bit integer argument to be added + * @return The sum of a + b + c + d + e + */ + safeAdd_32_5 = function (a, b, c, d, e) + { + var lsw = (a & 0xFFFF) + (b & 0xFFFF) + (c & 0xFFFF) + (d & 0xFFFF) + + (e & 0xFFFF), + msw = (a >>> 16) + (b >>> 16) + (c >>> 16) + (d >>> 16) + + (e >>> 16) + (lsw >>> 16); + + return ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + }, + + /* + * Add two 64-bit integers, wrapping at 2^64. This uses 16-bit operations + * internally to work around bugs in some JS interpreters. + * + * @private + * @param {Int_64} x The first 64-bit integer argument to be added + * @param {Int_64} y The second 64-bit integer argument to be added + * @return The sum of x + y + */ + safeAdd_64_2 = function (x, y) + { + var lsw, msw, lowOrder, highOrder; + + lsw = (x.lowOrder & 0xFFFF) + (y.lowOrder & 0xFFFF); + msw = (x.lowOrder >>> 16) + (y.lowOrder >>> 16) + (lsw >>> 16); + lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + + lsw = (x.highOrder & 0xFFFF) + (y.highOrder & 0xFFFF) + (msw >>> 16); + msw = (x.highOrder >>> 16) + (y.highOrder >>> 16) + (lsw >>> 16); + highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + + return new Int_64(highOrder, lowOrder); + }, + + /* + * Add four 64-bit integers, wrapping at 2^64. This uses 16-bit operations + * internally to work around bugs in some JS interpreters. + * + * @private + * @param {Int_64} a The first 64-bit integer argument to be added + * @param {Int_64} b The second 64-bit integer argument to be added + * @param {Int_64} c The third 64-bit integer argument to be added + * @param {Int_64} d The fouth 64-bit integer argument to be added + * @return The sum of a + b + c + d + */ + safeAdd_64_4 = function (a, b, c, d) + { + var lsw, msw, lowOrder, highOrder; + + lsw = (a.lowOrder & 0xFFFF) + (b.lowOrder & 0xFFFF) + + (c.lowOrder & 0xFFFF) + (d.lowOrder & 0xFFFF); + msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + + (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (lsw >>> 16); + lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + + lsw = (a.highOrder & 0xFFFF) + (b.highOrder & 0xFFFF) + + (c.highOrder & 0xFFFF) + (d.highOrder & 0xFFFF) + (msw >>> 16); + msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + + (c.highOrder >>> 16) + (d.highOrder >>> 16) + (lsw >>> 16); + highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + + return new Int_64(highOrder, lowOrder); + }, + + /* + * Add five 64-bit integers, wrapping at 2^64. This uses 16-bit operations + * internally to work around bugs in some JS interpreters. + * + * @private + * @param {Int_64} a The first 64-bit integer argument to be added + * @param {Int_64} b The second 64-bit integer argument to be added + * @param {Int_64} c The third 64-bit integer argument to be added + * @param {Int_64} d The fouth 64-bit integer argument to be added + * @param {Int_64} e The fouth 64-bit integer argument to be added + * @return The sum of a + b + c + d + e + */ + safeAdd_64_5 = function (a, b, c, d, e) + { + var lsw, msw, lowOrder, highOrder; + + lsw = (a.lowOrder & 0xFFFF) + (b.lowOrder & 0xFFFF) + + (c.lowOrder & 0xFFFF) + (d.lowOrder & 0xFFFF) + + (e.lowOrder & 0xFFFF); + msw = (a.lowOrder >>> 16) + (b.lowOrder >>> 16) + + (c.lowOrder >>> 16) + (d.lowOrder >>> 16) + (e.lowOrder >>> 16) + + (lsw >>> 16); + lowOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + + lsw = (a.highOrder & 0xFFFF) + (b.highOrder & 0xFFFF) + + (c.highOrder & 0xFFFF) + (d.highOrder & 0xFFFF) + + (e.highOrder & 0xFFFF) + (msw >>> 16); + msw = (a.highOrder >>> 16) + (b.highOrder >>> 16) + + (c.highOrder >>> 16) + (d.highOrder >>> 16) + + (e.highOrder >>> 16) + (lsw >>> 16); + highOrder = ((msw & 0xFFFF) << 16) | (lsw & 0xFFFF); + + return new Int_64(highOrder, lowOrder); + }, + + /* + * Calculates the SHA-1 hash of the string set at instantiation + * + * @private + * @param {Array} message The binary array representation of the string to + * hash + * @param {Number} messageLen The number of bits in the message + * @return The array of integers representing the SHA-1 hash of message + */ + coreSHA1 = function (message, messageLen) + { + var W = [], a, b, c, d, e, T, ch = ch_32, parity = parity_32, + maj = maj_32, rotl = rotl_32, safeAdd_2 = safeAdd_32_2, i, t, + safeAdd_5 = safeAdd_32_5, appendedMessageLength, + H = [ + 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476, 0xc3d2e1f0 + ], + K = [ + 0x5a827999, 0x5a827999, 0x5a827999, 0x5a827999, + 0x5a827999, 0x5a827999, 0x5a827999, 0x5a827999, + 0x5a827999, 0x5a827999, 0x5a827999, 0x5a827999, + 0x5a827999, 0x5a827999, 0x5a827999, 0x5a827999, + 0x5a827999, 0x5a827999, 0x5a827999, 0x5a827999, + 0x6ed9eba1, 0x6ed9eba1, 0x6ed9eba1, 0x6ed9eba1, + 0x6ed9eba1, 0x6ed9eba1, 0x6ed9eba1, 0x6ed9eba1, + 0x6ed9eba1, 0x6ed9eba1, 0x6ed9eba1, 0x6ed9eba1, + 0x6ed9eba1, 0x6ed9eba1, 0x6ed9eba1, 0x6ed9eba1, + 0x6ed9eba1, 0x6ed9eba1, 0x6ed9eba1, 0x6ed9eba1, + 0x8f1bbcdc, 0x8f1bbcdc, 0x8f1bbcdc, 0x8f1bbcdc, + 0x8f1bbcdc, 0x8f1bbcdc, 0x8f1bbcdc, 0x8f1bbcdc, + 0x8f1bbcdc, 0x8f1bbcdc, 0x8f1bbcdc, 0x8f1bbcdc, + 0x8f1bbcdc, 0x8f1bbcdc, 0x8f1bbcdc, 0x8f1bbcdc, + 0x8f1bbcdc, 0x8f1bbcdc, 0x8f1bbcdc, 0x8f1bbcdc, + 0xca62c1d6, 0xca62c1d6, 0xca62c1d6, 0xca62c1d6, + 0xca62c1d6, 0xca62c1d6, 0xca62c1d6, 0xca62c1d6, + 0xca62c1d6, 0xca62c1d6, 0xca62c1d6, 0xca62c1d6, + 0xca62c1d6, 0xca62c1d6, 0xca62c1d6, 0xca62c1d6, + 0xca62c1d6, 0xca62c1d6, 0xca62c1d6, 0xca62c1d6 + ]; + + /* Append '1' at the end of the binary string */ + message[messageLen >> 5] |= 0x80 << (24 - (messageLen % 32)); + /* Append length of binary string in the position such that the new + length is a multiple of 512. Logic does not work for even multiples + of 512 but there can never be even multiples of 512 */ + message[(((messageLen + 65) >> 9) << 4) + 15] = messageLen; + + appendedMessageLength = message.length; + + for (i = 0; i < appendedMessageLength; i += 16) + { + a = H[0]; + b = H[1]; + c = H[2]; + d = H[3]; + e = H[4]; + + for (t = 0; t < 80; t += 1) + { + if (t < 16) + { + W[t] = message[t + i]; + } + else + { + W[t] = rotl(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1); + } + + if (t < 20) + { + T = safeAdd_5(rotl(a, 5), ch(b, c, d), e, K[t], W[t]); + } + else if (t < 40) + { + T = safeAdd_5(rotl(a, 5), parity(b, c, d), e, K[t], W[t]); + } + else if (t < 60) + { + T = safeAdd_5(rotl(a, 5), maj(b, c, d), e, K[t], W[t]); + } else { + T = safeAdd_5(rotl(a, 5), parity(b, c, d), e, K[t], W[t]); + } + + e = d; + d = c; + c = rotl(b, 30); + b = a; + a = T; + } + + H[0] = safeAdd_2(a, H[0]); + H[1] = safeAdd_2(b, H[1]); + H[2] = safeAdd_2(c, H[2]); + H[3] = safeAdd_2(d, H[3]); + H[4] = safeAdd_2(e, H[4]); + } + + return H; + }, + + /* + * Calculates the desired SHA-2 hash of the string set at instantiation + * + * @private + * @param {Array} The binary array representation of the string to hash + * @param {Number} The number of bits in message + * @param {String} variant The desired SHA-2 variant + * @return The array of integers representing the SHA-2 hash of message + */ + coreSHA2 = function (message, messageLen, variant) + { + var a, b, c, d, e, f, g, h, T1, T2, H, numRounds, lengthPosition, i, t, + binaryStringInc, binaryStringMult, safeAdd_2, safeAdd_4, safeAdd_5, + gamma0, gamma1, sigma0, sigma1, ch, maj, Int, K, W = [], + appendedMessageLength; + + /* Set up the various function handles and variable for the specific + * variant */ + if (variant === "SHA-224" || variant === "SHA-256") + { + /* 32-bit variant */ + numRounds = 64; + lengthPosition = (((messageLen + 65) >> 9) << 4) + 15; + binaryStringInc = 16; + binaryStringMult = 1; + Int = Number; + safeAdd_2 = safeAdd_32_2; + safeAdd_4 = safeAdd_32_4; + safeAdd_5 = safeAdd_32_5; + gamma0 = gamma0_32; + gamma1 = gamma1_32; + sigma0 = sigma0_32; + sigma1 = sigma1_32; + maj = maj_32; + ch = ch_32; + K = [ + 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, + 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, + 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, + 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, + 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, + 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, + 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, + 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, + 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, + 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, + 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, + 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, + 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, + 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, + 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, + 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 + ]; + + if (variant === "SHA-224") + { + H = [ + 0xc1059ed8, 0x367cd507, 0x3070dd17, 0xf70e5939, + 0xffc00b31, 0x68581511, 0x64f98fa7, 0xbefa4fa4 + ]; + } + else + { + H = [ + 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, + 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 + ]; + } + } + else if (variant === "SHA-384" || variant === "SHA-512") + { + /* 64-bit variant */ + numRounds = 80; + lengthPosition = (((messageLen + 128) >> 10) << 5) + 31; + binaryStringInc = 32; + binaryStringMult = 2; + Int = Int_64; + safeAdd_2 = safeAdd_64_2; + safeAdd_4 = safeAdd_64_4; + safeAdd_5 = safeAdd_64_5; + gamma0 = gamma0_64; + gamma1 = gamma1_64; + sigma0 = sigma0_64; + sigma1 = sigma1_64; + maj = maj_64; + ch = ch_64; + + K = [ + new Int(0x428a2f98, 0xd728ae22), new Int(0x71374491, 0x23ef65cd), + new Int(0xb5c0fbcf, 0xec4d3b2f), new Int(0xe9b5dba5, 0x8189dbbc), + new Int(0x3956c25b, 0xf348b538), new Int(0x59f111f1, 0xb605d019), + new Int(0x923f82a4, 0xaf194f9b), new Int(0xab1c5ed5, 0xda6d8118), + new Int(0xd807aa98, 0xa3030242), new Int(0x12835b01, 0x45706fbe), + new Int(0x243185be, 0x4ee4b28c), new Int(0x550c7dc3, 0xd5ffb4e2), + new Int(0x72be5d74, 0xf27b896f), new Int(0x80deb1fe, 0x3b1696b1), + new Int(0x9bdc06a7, 0x25c71235), new Int(0xc19bf174, 0xcf692694), + new Int(0xe49b69c1, 0x9ef14ad2), new Int(0xefbe4786, 0x384f25e3), + new Int(0x0fc19dc6, 0x8b8cd5b5), new Int(0x240ca1cc, 0x77ac9c65), + new Int(0x2de92c6f, 0x592b0275), new Int(0x4a7484aa, 0x6ea6e483), + new Int(0x5cb0a9dc, 0xbd41fbd4), new Int(0x76f988da, 0x831153b5), + new Int(0x983e5152, 0xee66dfab), new Int(0xa831c66d, 0x2db43210), + new Int(0xb00327c8, 0x98fb213f), new Int(0xbf597fc7, 0xbeef0ee4), + new Int(0xc6e00bf3, 0x3da88fc2), new Int(0xd5a79147, 0x930aa725), + new Int(0x06ca6351, 0xe003826f), new Int(0x14292967, 0x0a0e6e70), + new Int(0x27b70a85, 0x46d22ffc), new Int(0x2e1b2138, 0x5c26c926), + new Int(0x4d2c6dfc, 0x5ac42aed), new Int(0x53380d13, 0x9d95b3df), + new Int(0x650a7354, 0x8baf63de), new Int(0x766a0abb, 0x3c77b2a8), + new Int(0x81c2c92e, 0x47edaee6), new Int(0x92722c85, 0x1482353b), + new Int(0xa2bfe8a1, 0x4cf10364), new Int(0xa81a664b, 0xbc423001), + new Int(0xc24b8b70, 0xd0f89791), new Int(0xc76c51a3, 0x0654be30), + new Int(0xd192e819, 0xd6ef5218), new Int(0xd6990624, 0x5565a910), + new Int(0xf40e3585, 0x5771202a), new Int(0x106aa070, 0x32bbd1b8), + new Int(0x19a4c116, 0xb8d2d0c8), new Int(0x1e376c08, 0x5141ab53), + new Int(0x2748774c, 0xdf8eeb99), new Int(0x34b0bcb5, 0xe19b48a8), + new Int(0x391c0cb3, 0xc5c95a63), new Int(0x4ed8aa4a, 0xe3418acb), + new Int(0x5b9cca4f, 0x7763e373), new Int(0x682e6ff3, 0xd6b2b8a3), + new Int(0x748f82ee, 0x5defb2fc), new Int(0x78a5636f, 0x43172f60), + new Int(0x84c87814, 0xa1f0ab72), new Int(0x8cc70208, 0x1a6439ec), + new Int(0x90befffa, 0x23631e28), new Int(0xa4506ceb, 0xde82bde9), + new Int(0xbef9a3f7, 0xb2c67915), new Int(0xc67178f2, 0xe372532b), + new Int(0xca273ece, 0xea26619c), new Int(0xd186b8c7, 0x21c0c207), + new Int(0xeada7dd6, 0xcde0eb1e), new Int(0xf57d4f7f, 0xee6ed178), + new Int(0x06f067aa, 0x72176fba), new Int(0x0a637dc5, 0xa2c898a6), + new Int(0x113f9804, 0xbef90dae), new Int(0x1b710b35, 0x131c471b), + new Int(0x28db77f5, 0x23047d84), new Int(0x32caab7b, 0x40c72493), + new Int(0x3c9ebe0a, 0x15c9bebc), new Int(0x431d67c4, 0x9c100d4c), + new Int(0x4cc5d4be, 0xcb3e42b6), new Int(0x597f299c, 0xfc657e2a), + new Int(0x5fcb6fab, 0x3ad6faec), new Int(0x6c44198c, 0x4a475817) + ]; + + if (variant === "SHA-384") + { + H = [ + new Int(0xcbbb9d5d, 0xc1059ed8), new Int(0x0629a292a, 0x367cd507), + new Int(0x9159015a, 0x3070dd17), new Int(0x0152fecd8, 0xf70e5939), + new Int(0x67332667, 0xffc00b31), new Int(0x98eb44a87, 0x68581511), + new Int(0xdb0c2e0d, 0x64f98fa7), new Int(0x047b5481d, 0xbefa4fa4) + ]; + } + else + { + H = [ + new Int(0x6a09e667, 0xf3bcc908), new Int(0xbb67ae85, 0x84caa73b), + new Int(0x3c6ef372, 0xfe94f82b), new Int(0xa54ff53a, 0x5f1d36f1), + new Int(0x510e527f, 0xade682d1), new Int(0x9b05688c, 0x2b3e6c1f), + new Int(0x1f83d9ab, 0xfb41bd6b), new Int(0x5be0cd19, 0x137e2179) + ]; + } + } + + /* Append '1' at the end of the binary string */ + message[messageLen >> 5] |= 0x80 << (24 - messageLen % 32); + /* Append length of binary string in the position such that the new + * length is correct */ + message[lengthPosition] = messageLen; + + appendedMessageLength = message.length; + + for (i = 0; i < appendedMessageLength; i += binaryStringInc) + { + a = H[0]; + b = H[1]; + c = H[2]; + d = H[3]; + e = H[4]; + f = H[5]; + g = H[6]; + h = H[7]; + + for (t = 0; t < numRounds; t += 1) + { + if (t < 16) + { + /* Bit of a hack - for 32-bit, the second term is ignored */ + W[t] = new Int(message[t * binaryStringMult + i], + message[t * binaryStringMult + i + 1]); + } + else + { + W[t] = safeAdd_4( + gamma1(W[t - 2]), W[t - 7], + gamma0(W[t - 15]), W[t - 16] + ); + } + + T1 = safeAdd_5(h, sigma1(e), ch(e, f, g), K[t], W[t]); + T2 = safeAdd_2(sigma0(a), maj(a, b, c)); + h = g; + g = f; + f = e; + e = safeAdd_2(d, T1); + d = c; + c = b; + b = a; + a = safeAdd_2(T1, T2); + } + + H[0] = safeAdd_2(a, H[0]); + H[1] = safeAdd_2(b, H[1]); + H[2] = safeAdd_2(c, H[2]); + H[3] = safeAdd_2(d, H[3]); + H[4] = safeAdd_2(e, H[4]); + H[5] = safeAdd_2(f, H[5]); + H[6] = safeAdd_2(g, H[6]); + H[7] = safeAdd_2(h, H[7]); + } + + switch (variant) + { + case "SHA-224": + return [ + H[0], H[1], H[2], H[3], + H[4], H[5], H[6] + ]; + case "SHA-256": + return H; + case "SHA-384": + return [ + H[0].highOrder, H[0].lowOrder, + H[1].highOrder, H[1].lowOrder, + H[2].highOrder, H[2].lowOrder, + H[3].highOrder, H[3].lowOrder, + H[4].highOrder, H[4].lowOrder, + H[5].highOrder, H[5].lowOrder + ]; + case "SHA-512": + return [ + H[0].highOrder, H[0].lowOrder, + H[1].highOrder, H[1].lowOrder, + H[2].highOrder, H[2].lowOrder, + H[3].highOrder, H[3].lowOrder, + H[4].highOrder, H[4].lowOrder, + H[5].highOrder, H[5].lowOrder, + H[6].highOrder, H[6].lowOrder, + H[7].highOrder, H[7].lowOrder + ]; + default: + /* This should never be reached */ + return []; + } + }, + + /* + * jsSHA is the workhorse of the library. Instantiate it with the string to + * be hashed as the parameter + * + * @constructor + * @param {String} srcString The string to be hashed + * @param {String} inputFormat The format of srcString, ASCII or HEX + */ + jsSHA = function (srcString, inputFormat) + { + + this.sha1 = null; + this.sha224 = null; + this.sha256 = null; + this.sha384 = null; + this.sha512 = null; + + this.strBinLen = null; + this.strToHash = null; + + /* Convert the input string into the correct type */ + if ("HEX" === inputFormat) + { + if (0 !== (srcString.length % 2)) + { + return "TEXT MUST BE IN BYTE INCREMENTS"; + } + this.strBinLen = srcString.length * 4; + this.strToHash = hex2binb(srcString); + } + else if (("ASCII" === inputFormat) || + ('undefined' === typeof(inputFormat))) + { + this.strBinLen = srcString.length * charSize; + this.strToHash = str2binb(srcString); + } + else + { + return "UNKNOWN TEXT INPUT TYPE"; + } + }; + + jsSHA.prototype = { + /* + * Returns the desired SHA hash of the string specified at instantiation + * using the specified parameters + * + * @param {String} variant The desired SHA variant (SHA-1, SHA-224, + * SHA-256, SHA-384, or SHA-512) + * @param {String} format The desired output formatting (B64 or HEX) + * @return The string representation of the hash in the format specified + */ + getHash : function (variant, format) + { + var formatFunc = null, message = this.strToHash.slice(); + + switch (format) + { + case "HEX": + formatFunc = binb2hex; + break; + case "B64": + formatFunc = binb2b64; + break; + case "ASCII": + formatFunc = binb2str; + break; + default: + return "FORMAT NOT RECOGNIZED"; + } + + switch (variant) + { + case "SHA-1": + if (null === this.sha1) + { + this.sha1 = coreSHA1(message, this.strBinLen); + } + return formatFunc(this.sha1); + case "SHA-224": + if (null === this.sha224) + { + this.sha224 = coreSHA2(message, this.strBinLen, variant); + } + return formatFunc(this.sha224); + case "SHA-256": + if (null === this.sha256) + { + this.sha256 = coreSHA2(message, this.strBinLen, variant); + } + return formatFunc(this.sha256); + case "SHA-384": + if (null === this.sha384) + { + this.sha384 = coreSHA2(message, this.strBinLen, variant); + } + return formatFunc(this.sha384); + case "SHA-512": + if (null === this.sha512) + { + this.sha512 = coreSHA2(message, this.strBinLen, variant); + } + return formatFunc(this.sha512); + default: + return "HASH NOT RECOGNIZED"; + } + }, + + /* + * Returns the desired HMAC of the string specified at instantiation + * using the key and variant param. + * + * @param {String} key The key used to calculate the HMAC + * @param {String} inputFormat The format of key, ASCII or HEX + * @param {String} variant The desired SHA variant (SHA-1, SHA-224, + * SHA-256, SHA-384, or SHA-512) + * @param {String} outputFormat The desired output formatting + * (B64 or HEX) + * @return The string representation of the hash in the format specified + */ + getHMAC : function (key, inputFormat, variant, outputFormat) + { + var formatFunc, keyToUse, blockByteSize, blockBitSize, i, + retVal, lastArrayIndex, keyBinLen, hashBitSize, + keyWithIPad = [], keyWithOPad = []; + + /* Validate the output format selection */ + switch (outputFormat) + { + case "HEX": + formatFunc = binb2hex; + break; + case "B64": + formatFunc = binb2b64; + break; + case "ASCII": + formatFunc = binb2str; + break; + default: + return "FORMAT NOT RECOGNIZED"; + } + + /* Validate the hash variant selection and set needed variables */ + switch (variant) + { + case "SHA-1": + blockByteSize = 64; + hashBitSize = 160; + break; + case "SHA-224": + blockByteSize = 64; + hashBitSize = 224; + break; + case "SHA-256": + blockByteSize = 64; + hashBitSize = 256; + break; + case "SHA-384": + blockByteSize = 128; + hashBitSize = 384; + break; + case "SHA-512": + blockByteSize = 128; + hashBitSize = 512; + break; + default: + return "HASH NOT RECOGNIZED"; + } + + /* Validate input format selection */ + if ("HEX" === inputFormat) + { + /* Nibbles must come in pairs */ + if (0 !== (key.length % 2)) + { + return "KEY MUST BE IN BYTE INCREMENTS"; + } + keyToUse = hex2binb(key); + keyBinLen = key.length * 4; + } + else if ("ASCII" === inputFormat) + { + keyToUse = str2binb(key); + keyBinLen = key.length * charSize; + } + else + { + return "UNKNOWN KEY INPUT TYPE"; + } + + /* These are used multiple times, calculate and store them */ + blockBitSize = blockByteSize * 8; + lastArrayIndex = (blockByteSize / 4) - 1; + + /* Figure out what to do with the key based on its size relative to + * the hash's block size */ + if (blockByteSize < (keyBinLen / 8)) + { + if ("SHA-1" === variant) + { + keyToUse = coreSHA1(keyToUse, keyBinLen); + } + else + { + keyToUse = coreSHA2(keyToUse, keyBinLen, variant); + } + /* For all variants, the block size is bigger than the output + * size so there will never be a useful byte at the end of the + * string */ + keyToUse[lastArrayIndex] &= 0xFFFFFF00; + } + else if (blockByteSize > (keyBinLen / 8)) + { + /* If the blockByteSize is greater than the key length, there + * will always be at LEAST one "useless" byte at the end of the + * string */ + keyToUse[lastArrayIndex] &= 0xFFFFFF00; + } + + /* Create ipad and opad */ + for (i = 0; i <= lastArrayIndex; i += 1) + { + keyWithIPad[i] = keyToUse[i] ^ 0x36363636; + keyWithOPad[i] = keyToUse[i] ^ 0x5C5C5C5C; + } + + /* Calculate the HMAC */ + if ("SHA-1" === variant) + { + retVal = coreSHA1( + keyWithIPad.concat(this.strToHash), + blockBitSize + this.strBinLen); + retVal = coreSHA1( + keyWithOPad.concat(retVal), + blockBitSize + hashBitSize); + } + else + { + retVal = coreSHA2( + keyWithIPad.concat(this.strToHash), + blockBitSize + this.strBinLen, variant); + retVal = coreSHA2( + keyWithOPad.concat(retVal), + blockBitSize + hashBitSize, variant); + } + + return (formatFunc(retVal)); + } + }; + + window.jsSHA = jsSHA; +}()); + +function str_sha1(str) { + var shaObj = new jsSHA(str, "ASCII"); + return shaObj.getHash("SHA-1", "ASCII"); +} + +function str_sha224(str) { + var shaObj = new jsSHA(str, "ASCII"); + return shaObj.getHash("SHA-224", "ASCII"); +} + +function str_sha256(str) { + var shaObj = new jsSHA(str, "ASCII"); + return shaObj.getHash("SHA-256", "ASCII"); +} + + +function str_sha384(str) { + var shaObj = new jsSHA(str, "ASCII"); + return shaObj.getHash("SHA-384", "ASCII"); + +} + +function str_sha512(str) { + var shaObj = new jsSHA(str, "ASCII"); + return shaObj.getHash("SHA-512", "ASCII"); +} diff --git a/src/ciphers/openpgp.cfb.js b/src/ciphers/openpgp.cfb.js new file mode 100755 index 00000000..5078c6e6 --- /dev/null +++ b/src/ciphers/openpgp.cfb.js @@ -0,0 +1,275 @@ +// Modified by Recurity Labs GmbH + +// modified version of http://www.hanewin.net/encrypt/PGdecode.js: + +/* OpenPGP encryption using RSA/AES + * Copyright 2005-2006 Herbert Hanewinkel, www.haneWIN.de + * version 2.0, check www.haneWIN.de for the latest version + + * This software is provided as-is, without express or implied warranty. + * Permission to use, copy, modify, distribute or sell this software, with or + * without fee, for any purpose and by any individual or organization, is hereby + * granted, provided that the above copyright notice and this paragraph appear + * in all copies. Distribution as a part of an application or binary must + * include the above copyright notice in the documentation and/or other + * materials provided with the application or distribution. + */ + +// -------------------------------------- +/** + * This function encrypts a given with the specified prefixrandom + * using the specified blockcipher to encrypt a message + * @param prefixrandom random bytes of block_size length provided + * as a string to be used in prefixing the data + * @param blockcipherfn the algorithm encrypt function to encrypt + * data in one block_size encryption. The function must be + * specified as blockcipherfn([integer_array(integers 0..255)] + * block,[integer_array(integers 0..255)] key) returning an + * array of bytes (integers 0..255) + * @param block_size the block size in bytes of the algorithm used + * @param plaintext data to be encrypted provided as a string + * @param key key to be used to encrypt the data as + * integer_array(integers 0..255)]. This will be passed to the + * blockcipherfn + * @param resync a boolean value specifying if a resync of the + * IV should be used or not. The encrypteddatapacket uses the + * "old" style with a resync. Encryption within an + * encryptedintegrityprotecteddata packet is not resyncing the IV. + * @return a string with the encrypted data + */ +function openpgp_cfb_encrypt(prefixrandom, blockcipherencryptfn, plaintext, block_size, key, resync) { + var FR = new Array(block_size); + var FRE = new Array(block_size); + + prefixrandom = prefixrandom + prefixrandom.charAt(block_size-2) +prefixrandom.charAt(block_size-1); + util.print_debug("prefixrandom:"+util.hexstrdump(prefixrandom)); + var ciphertext = ""; + // 1. The feedback register (FR) is set to the IV, which is all zeros. + for (var i = 0; i < block_size; i++) FR[i] = 0; + + // 2. FR is encrypted to produce FRE (FR Encrypted). This is the + // encryption of an all-zero value. + FRE = blockcipherencryptfn(FR, key); + // 3. FRE is xored with the first BS octets of random data prefixed to + // the plaintext to produce C[1] through C[BS], the first BS octets + // of ciphertext. + for (var i = 0; i < block_size; i++) ciphertext += String.fromCharCode(FRE[i] ^ prefixrandom.charCodeAt(i)); + + // 4. FR is loaded with C[1] through C[BS]. + for (var i = 0; i < block_size; i++) FR[i] = ciphertext.charCodeAt(i); + + // 5. FR is encrypted to produce FRE, the encryption of the first BS + // octets of ciphertext. + FRE = blockcipherencryptfn(FR, key); + + // 6. The left two octets of FRE get xored with the next two octets of + // data that were prefixed to the plaintext. This produces C[BS+1] + // and C[BS+2], the next two octets of ciphertext. + ciphertext += String.fromCharCode(FRE[0] ^ prefixrandom.charCodeAt(block_size)); + ciphertext += String.fromCharCode(FRE[1] ^ prefixrandom.charCodeAt(block_size+1)); + + if (resync) { + // 7. (The resync step) FR is loaded with C3-C10. + for (var i = 0; i < block_size; i++) FR[i] = ciphertext.charCodeAt(i+2); + } else { + for (var i = 0; i < block_size; i++) FR[i] = ciphertext.charCodeAt(i); + } + // 8. FR is encrypted to produce FRE. + FRE = blockcipherencryptfn(FR, key); + + if (resync) { + // 9. FRE is xored with the first 8 octets of the given plaintext, now + // that we have finished encrypting the 10 octets of prefixed data. + // This produces C11-C18, the next 8 octets of ciphertext. + for (var i = 0; i < block_size; i++) + ciphertext += String.fromCharCode(FRE[i] ^ plaintext.charCodeAt(i)); + for(n=block_size+2; n < plaintext.length; n+=block_size) { + // 10. FR is loaded with C11-C18 + for (var i = 0; i < block_size; i++) FR[i] = ciphertext.charCodeAt(n+i); + + // 11. FR is encrypted to produce FRE. + FRE = blockcipherencryptfn(FR, key); + + // 12. FRE is xored with the next 8 octets of plaintext, to produce the + // next 8 octets of ciphertext. These are loaded into FR and the + // process is repeated until the plaintext is used up. + for (var i = 0; i < block_size; i++) ciphertext += String.fromCharCode(FRE[i] ^ plaintext.charCodeAt((n-2)+i)); + } + } + else { + plaintext = " "+plaintext; + // 9. FRE is xored with the first 8 octets of the given plaintext, now + // that we have finished encrypting the 10 octets of prefixed data. + // This produces C11-C18, the next 8 octets of ciphertext. + for (var i = 2; i < block_size; i++) ciphertext += String.fromCharCode(FRE[i] ^ plaintext.charCodeAt(i)); + + for(n=block_size; n block_size*pos) { + blocka = plaintext.substring((pos*block_size),(pos*block_size)+block_size); + var encblock = blockcipherencryptfn(blocki, key); + for (var i=0; i < blocka.size; i++) + blocki[i] = blocka ^ enblock(); + cyphertext += blocki; + pos++; + } + return cyphertext; +} + +function normal_cfb_decrypt(blockcipherencryptfn, block_size, key, ciphertext, iv) { + var blockp =""; + var pos = 0; + var plaintext = ""; + var offset = 0; + if (iv == null) + for (var i = 0; i < block_size; i++) blockp += String.fromCharCode(0); + else + blockp = iv.substring(0,block_size); + while (ciphertext.length > (block_size*pos)) { + var decblock = blockcipherencryptfn(blockp, key); + blockp = ciphertext.substring((pos*(block_size))+offset,(pos*(block_size))+(block_size)+offset); + for (var i=0; i < blockp.length; i++) { + plaintext += String.fromCharCode(blockp.charCodeAt(i) ^ decblock[i]); + } + pos++; + } + + return plaintext; +} diff --git a/src/ciphers/openpgp.crypto.js b/src/ciphers/openpgp.crypto.js new file mode 100755 index 00000000..b18cd0a9 --- /dev/null +++ b/src/ciphers/openpgp.crypto.js @@ -0,0 +1,458 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +// The GPG4Browsers crypto interface + +/** + * Encrypts data using the specified public key multiprecision integers + * and the specified algorithm. + * @param algo [Integer] Algorithm to be used (See RFC4880 9.1) + * @param publicMPIs [Array[openpgp_type_mpi]] algorithm dependent multiprecision integers + * @param data [openpgp_type_mpi] data to be encrypted as MPI + * @return [Object] if RSA an openpgp_type_mpi; if elgamal encryption an array of two + * openpgp_type_mpi is returned; otherwise null + */ +function openpgp_crypto_asymetricEncrypt(algo, publicMPIs, data) { + switch(algo) { + case 1: // RSA (Encrypt or Sign) [HAC] + case 2: // RSA Encrypt-Only [HAC] + case 3: // RSA Sign-Only [HAC] + var rsa = new RSA(); + var n = publicMPIs[0].toBigInteger(); + var e = publicMPIs[1].toBigInteger(); + var m = data.toBigInteger(); + return rsa.encrypt(m,e,n).toMPI(); + case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] + var elgamal = new Elgamal(); + var p = publicMPIs[0].toBigInteger(); + var g = publicMPIs[1].toBigInteger(); + var y = publicMPIs[2].toBigInteger(); + var m = data.toBigInteger(); + return elgamal.encrypt(m,g,p,y); + default: + return null; + } +} + +/** + * Decrypts data using the specified public key multiprecision integers of the private key, + * the specified secretMPIs of the private key and the specified algorithm. + * @param algo [Integer] Algorithm to be used (See RFC4880 9.1) + * @param publicMPIs [Array[openpgp_type_mpi]] algorithm dependent multiprecision integers of the public key part of the private key + * @param secretMPIs [Array[openpgp_type_mpi]] algorithm dependent multiprecision integers of the private key used + * @param data [openpgp_type_mpi] data to be encrypted as MPI + * @return [BigInteger] returns a big integer containing the decrypted data; otherwise null + */ + +function openpgp_crypto_asymetricDecrypt(algo, publicMPIs, secretMPIs, dataMPIs) { + switch(algo) { + case 1: // RSA (Encrypt or Sign) [HAC] + case 2: // RSA Encrypt-Only [HAC] + case 3: // RSA Sign-Only [HAC] + var rsa = new RSA(); + var d = secretMPIs[0].toBigInteger(); + var p = secretMPIs[1].toBigInteger(); + var q = secretMPIs[2].toBigInteger(); + var u = secretMPIs[3].toBigInteger(); + var m = dataMPIs[0].toBigInteger(); + return rsa.decrypt(m, d, p, q, u); + case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] + var elgamal = new Elgamal(); + var x = secretMPIs[0].toBigInteger(); + var c1 = dataMPIs[0].toBigInteger(); + var c2 = dataMPIs[1].toBigInteger(); + var p = publicMPIs[0].toBigInteger(); + return elgamal.decrypt(c1,c2,p,x); + default: + return null; + } + +} + +/** + * Symmetrically encrypts data using prefixedrandom, a key with length + * depending on the algorithm in openpgp_cfb mode with or without resync + * (MDC style) + * @param prefixrandom secure random bytes as string in length equal to the + * block size of the algorithm used (use openpgp_crypto_getPrefixRandom(algo) + * to retrieve that string + * @param algo [Integer] algorithm to use (see RFC4880 9.2) + * @param key [String] key as string. length is depending on the algorithm used + * @param data [String] data to encrypt + * @param openpgp_cfb [boolean] + * @return [String] encrypted data + */ +function openpgp_crypto_symmetricEncrypt(prefixrandom, algo, key, data, openpgp_cfb) { + switch(algo) { + case 0: // Plaintext or unencrypted data + return data; // blockcipherencryptfn, plaintext, block_size, key + case 2: // TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192) + return openpgp_cfb_encrypt(prefixrandom, desede, data,8,key, openpgp_cfb).substring(0, data.length + 10); + case 3: // CAST5 (128 bit key, as per [RFC2144]) + return openpgp_cfb_encrypt(prefixrandom, cast5_encrypt, data,8,key, openpgp_cfb).substring(0, data.length + 10); + case 4: // Blowfish (128 bit key, 16 rounds) [BLOWFISH] + return openpgp_cfb_encrypt(prefixrandom, BFencrypt, data,8,key, openpgp_cfb).substring(0, data.length + 10); + case 7: // AES with 128-bit key [AES] + case 8: // AES with 192-bit key + case 9: // AES with 256-bit key + return openpgp_cfb_encrypt(prefixrandom, AESencrypt, data, 16, keyExpansion(key), openpgp_cfb).substring(0, data.length + 18); + case 10: // Twofish with 256-bit key [TWOFISH] + return openpgp_cfb_encrypt(prefixrandom, TFencrypt, data,16, key, openpgp_cfb).substring(0, data.length + 18); + case 1: // IDEA [IDEA] + util.print_error("IDEA Algorithm not implemented"); + return null; + default: + return null; + } +} + +/** + * generate random byte prefix as string for the specified algorithm + * @param algo [Integer] algorithm to use (see RFC4880 9.2) + * @return [String] random bytes with length equal to the block + * size of the cipher + */ +function openpgp_crypto_getPrefixRandom(algo) { + switch(algo) { + case 2: + case 3: + case 4: + return openpgp_crypto_getRandomBytes(8); + case 7: + case 8: + case 9: + case 10: + return openpgp_crypto_getRandomBytes(16); + default: + return null; + } +} +/** + * Symmetrically decrypts data using a key with length depending on the + * algorithm in openpgp_cfb mode with or without resync (MDC style) + * @param algo [Integer] algorithm to use (see RFC4880 9.2) + * @param key [String] key as string. length is depending on the algorithm used + * @param data [String] data to be decrypted + * @param openpgp_cfb [boolean] if true use the resync (for encrypteddata); + * otherwise use without the resync (for MDC encrypted data) + * @return [String] plaintext data + */ +function openpgp_crypto_symmetricDecrypt(algo, key, data, openpgp_cfb) { + util.print_debug("openpgp_crypto_symmetricDecrypt:\nalgo:"+algo+"\nencrypteddata:"+util.hexstrdump(data)); + var n = 0; + if (!openpgp_cfb) + n = 2; + switch(algo) { + case 0: // Plaintext or unencrypted data + return data; + case 2: // TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192) + return openpgp_cfb_decrypt(desede, 8, key, data, openpgp_cfb).substring(n, (data.length+n)-10); + case 3: // CAST5 (128 bit key, as per [RFC2144]) + return openpgp_cfb_decrypt(cast5_encrypt, 8, key, data, openpgp_cfb).substring(n, (data.length+n)-10); + case 4: // Blowfish (128 bit key, 16 rounds) [BLOWFISH] + return openpgp_cfb_decrypt(BFencrypt, 8, key, data, openpgp_cfb).substring(n, (data.length+n)-10); + case 7: // AES with 128-bit key [AES] + case 8: // AES with 192-bit key + case 9: // AES with 256-bit key + return openpgp_cfb_decrypt(AESencrypt, 16, keyExpansion(key), data, openpgp_cfb).substring(n, (data.length+n)-18); + case 10: // Twofish with 256-bit key [TWOFISH] + var result = openpgp_cfb_decrypt(TFencrypt, 16, key, data, openpgp_cfb).substring(n, (data.length+n)-18); + return result; + case 1: // IDEA [IDEA] + util.print_error(""+ (algo == 1 ? "IDEA Algorithm not implemented" : "Twofish Algorithm not implemented")); + return null; + default: + } + return null; +} + +/** + * retrieve the MDC prefixed bytes by decrypting them + * @param algo [Integer] algorithm to use (see RFC4880 9.2) + * @param key [String] key as string. length is depending on the algorithm used + * @param data [String] encrypted data where the prefix is decrypted from + * @return [String] plain text data of the prefixed data + */ +function openpgp_crypto_MDCSystemBytes(algo, key, data) { + util.print_debug("openpgp_crypto_symmetricDecrypt:\nencrypteddata:"+util.hexstrdump(data)); + switch(algo) { + case 0: // Plaintext or unencrypted data + return data; + case 2: // TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192) + return openpgp_cfb_mdc(desede, 8, key, data, openpgp_cfb); + case 3: // CAST5 (128 bit key, as per [RFC2144]) + return openpgp_cfb_mdc(cast5_encrypt, 8, key, data); + case 4: // Blowfish (128 bit key, 16 rounds) [BLOWFISH] + return openpgp_cfb_mdc(BFencrypt, 8, key, data); + case 7: // AES with 128-bit key [AES] + case 8: // AES with 192-bit key + case 9: // AES with 256-bit key + return openpgp_cfb_mdc(AESencrypt, 16, keyExpansion(key), data); + case 10: + return openpgp_cfb_mdc(TFencrypt, 16, key, data); + case 1: // IDEA [IDEA] + util.print_error(""+ (algo == 1 ? "IDEA Algorithm not implemented" : "Twofish Algorithm not implemented")); + return null; + default: + } + return null; +} +/** + * Generating a session key for the specified symmetric algorithm + * @param algo [Integer] algorithm to use (see RFC4880 9.2) + * @return [String] random bytes as a string to be used as a key + */ +function openpgp_crypto_generateSessionKey(algo) { + switch (algo) { + case 2: // TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192) + case 8: // AES with 192-bit key + return openpgp_crypto_getRandomBytes(24); + case 3: // CAST5 (128 bit key, as per [RFC2144]) + case 4: // Blowfish (128 bit key, 16 rounds) [BLOWFISH] + case 7: // AES with 128-bit key [AES] + util.print_debug("length = 16:\n"+util.hexstrdump(openpgp_crypto_getRandomBytes(16))); + return openpgp_crypto_getRandomBytes(16); + case 9: // AES with 256-bit key + case 10:// Twofish with 256-bit key [TWOFISH] + return openpgp_crypto_getRandomBytes(32); + } + return null; +} + +/** + * + * @param algo [Integer] public key algorithm + * @param hash_algo [Integer] hash algorithm + * @param msg_MPIs [Array[openpgp_type_mpi]] signature multiprecision integers + * @param publickey_MPIs [Array[openpgp_type_mpi]] public key multiprecision integers + * @param data [String] data on where the signature was computed on. + * @return true if signature (sig_data was equal to data over hash) + */ +function openpgp_crypto_verifySignature(algo, hash_algo, msg_MPIs, publickey_MPIs, data) { + var calc_hash = openpgp_crypto_hashData(hash_algo, data); + switch(algo) { + case 1: // RSA (Encrypt or Sign) [HAC] + case 2: // RSA Encrypt-Only [HAC] + case 3: // RSA Sign-Only [HAC] + var rsa = new RSA(); + var n = publickey_MPIs[0].toBigInteger(); + var e = publickey_MPIs[1].toBigInteger(); + var x = msg_MPIs[0].toBigInteger(); + var dopublic = rsa.verify(x,e,n); + var hash = openpgp_encoding_emsa_pkcs1_decode(hash_algo,dopublic.toMPI().substring(2)); + if (hash == -1) { + util.print_error("PKCS1 padding in message or key incorrect. Aborting..."); + return false; + } + return hash == calc_hash; + + case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] + util.print_error("signing with Elgamal is not defined in the OpenPGP standard."); + return null; + case 17: // DSA (Digital Signature Algorithm) [FIPS186] [HAC] + var dsa = new DSA(); + var s1 = msg_MPIs[0].toBigInteger(); + var s2 = msg_MPIs[1].toBigInteger(); + var p = publickey_MPIs[0].toBigInteger(); + var q = publickey_MPIs[1].toBigInteger(); + var g = publickey_MPIs[2].toBigInteger(); + var y = publickey_MPIs[3].toBigInteger(); + var m = data; + var dopublic = dsa.verify(hash_algo,s1,s2,m,p,q,g,y); + return dopublic.compareTo(s1) == 0; + default: + return null; + } + +} + +/** + * Create a signature on data using the specified algorithm + * @param hash_algo [Integer] hash algorithm to use (See RFC4880 9.4) + * @param algo [Integer] asymmetric cipher algorithm to use (See RFC4880 9.1) + * @param publicMPIs [Array[openpgp_type_mpi]] public key multiprecision integers of the private key + * @param secretMPIs [Array[openpgp_type_mpi]] private key multiprecision integers which is used to sign the data + * @param data [String] data to be signed + * @return [String or openpgp_type_mpi] + */ +function openpgp_crypto_signData(hash_algo, algo, publicMPIs, secretMPIs, data) { + + switch(algo) { + case 1: // RSA (Encrypt or Sign) [HAC] + case 2: // RSA Encrypt-Only [HAC] + case 3: // RSA Sign-Only [HAC] + var rsa = new RSA(); + var d = secretMPIs[0].toBigInteger(); + var n = publicMPIs[0].toBigInteger(); + var m = openpgp_encoding_emsa_pkcs1_encode(hash_algo, data,publicMPIs[0].mpiByteLength); + util.print_debug("signing using RSA"); + return rsa.sign(m, d, n).toMPI(); + case 17: // DSA (Digital Signature Algorithm) [FIPS186] [HAC] + var dsa = new DSA(); + util.print_debug("DSA Sign: q size in Bytes:"+publicMPIs[1].getByteLength()); + var p = publicMPIs[0].toBigInteger(); + var q = publicMPIs[1].toBigInteger(); + var g = publicMPIs[2].toBigInteger(); + var y = publicMPIs[3].toBigInteger(); + var x = secretMPIs[0].toBigInteger(); + var m = data; + var result = dsa.sign(hash_algo,m, g, p, q, x); + util.print_debug("signing using DSA\n result:"+util.hexstrdump(result[0])+"|"+util.hexstrdump(result[1])); + return result[0]+result[1]; + case 16: // Elgamal (Encrypt-Only) [ELGAMAL] [HAC] + util.print_debug("signing with Elgamal is not defined in the OpenPGP standard."); + return null; + default: + return null; + } +} + +/** + * create a hash on the specified data using the specified algorithm + * @param algo [Integer] hash algorithm type (see RFC4880 9.4) + * @param data [String] data to be hashed + * @return [String] hash value + */ +function openpgp_crypto_hashData(algo, data) { + var hash = null; + switch(algo) { + case 1: // - MD5 [HAC] + hash = MD5(data); + break; + case 2: // - SHA-1 [FIPS180] + hash = str_sha1(data); + break; + case 3: // - RIPE-MD/160 [HAC] + hash = RMDstring(data); + break; + case 8: // - SHA256 [FIPS180] + hash = str_sha256(data); + break; + case 9: // - SHA384 [FIPS180] + hash = str_sha384(data); + break; + case 10:// - SHA512 [FIPS180] + hash = str_sha512(data); + break; + case 11:// - SHA224 [FIPS180] + hash = str_sha224(data); + default: + break; + } + return hash; +} + +/** + * returns the hash size in bytes of the specified hash algorithm type + * @param algo [Integer] hash algorithm type (See RFC4880 9.4) + * @return [Integer] size in bytes of the resulting hash + */ +function openpgp_crypto_getHashByteLength(algo) { + var hash = null; + switch(algo) { + case 1: // - MD5 [HAC] + return 16; + case 2: // - SHA-1 [FIPS180] + case 3: // - RIPE-MD/160 [HAC] + return 20; + case 8: // - SHA256 [FIPS180] + return 32; + case 9: // - SHA384 [FIPS180] + return 48 + case 10:// - SHA512 [FIPS180] + return 64; + case 11:// - SHA224 [FIPS180] + return 28; + } + return null; +} + +/** + * retrieve secure random byte string of the specified length + * @param length [Integer] length in bytes to generate + * @return [String] random byte string + */ +function openpgp_crypto_getRandomBytes(length) { + var result = ''; + for (var i = 0; i < length; i++) { + result += String.fromCharCode(openpgp_crypto_getSecureRandomOctet()); + } + return result; +} + +/** + * return a pseudo-random number in the specified range + * @param from [Integer] min of the random number + * @param to [Integer] max of the random number (max 32bit) + * @return [Integer] a pseudo random number + */ +function openpgp_crypto_getPseudoRandom(from, to) { + return Math.round(Math.random()*(to-from))+from; +} + +/** + * return a secure random number in the specified range + * @param from [Integer] min of the random number + * @param to [Integer] max of the random number (max 32bit) + * @return [Integer] a secure random number + */ +function openpgp_crypto_getSecureRandom(from, to) { + var buf = new Uint32Array(1); + window.crypto.getRandomValues(buf); + var bits = ((to-from)).toString(2).length; + while ((buf[0] & (Math.pow(2, bits) -1)) > (to-from)) + window.crypto.getRandomValues(buf); + return from+(Math.abs(buf[0] & (Math.pow(2, bits) -1))); +} + +function openpgp_crypto_getSecureRandomOctet() { + var buf = new Uint32Array(1); + window.crypto.getRandomValues(buf); + return buf[0] & 0xFF; +} + +/** + * create a secure random big integer of bits length + * @param bits [Integer] bit length of the MPI to create + * @return [BigInteger] resulting big integer + */ +function openpgp_crypto_getRandomBigInteger(bits) { + if (bits < 0) + return null; + var numBytes = Math.floor((bits+7)/8); + + var randomBits = openpgp_crypto_getRandomBytes(numBytes); + if (bits % 8 > 0) { + + randomBits = String.fromCharCode( + (Math.pow(2,bits % 8)-1) & + randomBits.charCodeAt(0)) + + randomBits.substring(1); + } + return new openpgp_type_mpi().create(randomBits).toBigInteger(); +} + +function openpgp_crypto_getRandomBigIntegerInRange(min, max) { + if (max < min) + return; + var range = max.subtract(min); + var r = openpgp_crypto_getRandomBigInteger(range.bitLength()); + while (r > range) { + r = openpgp_crypto_getRandomBigInteger(range.bitLength()); + } + return min.add(r); +} diff --git a/src/ciphers/symmetric/aes.js b/src/ciphers/symmetric/aes.js new file mode 100755 index 00000000..bbbc0bea --- /dev/null +++ b/src/ciphers/symmetric/aes.js @@ -0,0 +1,484 @@ + +/* Rijndael (AES) Encryption + * Copyright 2005 Herbert Hanewinkel, www.haneWIN.de + * version 1.1, check www.haneWIN.de for the latest version + + * This software is provided as-is, without express or implied warranty. + * Permission to use, copy, modify, distribute or sell this software, with or + * without fee, for any purpose and by any individual or organization, is hereby + * granted, provided that the above copyright notice and this paragraph appear + * in all copies. Distribution as a part of an application or binary must + * include the above copyright notice in the documentation and/or other + * materials provided with the application or distribution. + */ + +// The round constants used in subkey expansion +var Rcon = [ +0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c, 0xd8, +0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4, +0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91 ]; + +// Precomputed lookup table for the SBox +var S = [ + 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, +118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164, +114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, +216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, +235, 39, 178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, +179, 41, 227, 47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, +190, 57, 74, 76, 88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, +249, 2, 127, 80, 60, 159, 168, 81, 163, 64, 143, 146, 157, 56, 245, +188, 182, 218, 33, 16, 255, 243, 210, 205, 12, 19, 236, 95, 151, 68, +23, 196, 167, 126, 61, 100, 93, 25, 115, 96, 129, 79, 220, 34, 42, +144, 136, 70, 238, 184, 20, 222, 94, 11, 219, 224, 50, 58, 10, 73, + 6, 36, 92, 194, 211, 172, 98, 145, 149, 228, 121, 231, 200, 55, 109, +141, 213, 78, 169, 108, 86, 244, 234, 101, 122, 174, 8, 186, 120, 37, + 46, 28, 166, 180, 198, 232, 221, 116, 31, 75, 189, 139, 138, 112, 62, +181, 102, 72, 3, 246, 14, 97, 53, 87, 185, 134, 193, 29, 158, 225, +248, 152, 17, 105, 217, 142, 148, 155, 30, 135, 233, 206, 85, 40, 223, +140, 161, 137, 13, 191, 230, 66, 104, 65, 153, 45, 15, 176, 84, 187, + 22 ]; + +var T1 = [ +0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, +0x0df2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, +0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56, +0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, +0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, +0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, +0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, +0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, +0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, +0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, +0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, +0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, +0x0c040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, +0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f, +0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, +0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, +0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, +0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, +0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, +0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, +0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, +0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, +0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, +0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85, +0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, +0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, +0xcf45458a, 0x10f9f9e9, 0x06020204, 0x817f7ffe, +0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, +0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05, +0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, +0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, +0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, +0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, +0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, +0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, +0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, +0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, +0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b, +0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, +0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, +0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, +0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8, +0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, +0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, +0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, +0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, +0xb46c6cd8, 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, +0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, +0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, +0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, +0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, +0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, +0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, +0xd8484890, 0x05030306, 0x01f6f6f7, 0x120e0e1c, +0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, +0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, +0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, +0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, +0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, +0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, +0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, +0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, +0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, +0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c ]; + +var T2 = [ +0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, +0xf2f2ff0d, 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, +0x30306050, 0x01010203, 0x6767cea9, 0x2b2b567d, +0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a, +0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, +0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, +0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, +0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b, +0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a, +0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, +0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, +0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, +0x0404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e, +0x18183028, 0x969637a1, 0x05050a0f, 0x9a9a2fb5, +0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, +0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, +0x0909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e, +0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, +0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, +0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397, +0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, +0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, +0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b, +0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a, +0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, +0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, +0x45458acf, 0xf9f9e910, 0x02020406, 0x7f7ffe81, +0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, +0x5151a2f3, 0xa3a35dfe, 0x404080c0, 0x8f8f058a, +0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, +0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, +0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, +0xcdcd814c, 0x0c0c1814, 0x13132635, 0xececc32f, +0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, +0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, +0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, +0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, +0x22224466, 0x2a2a547e, 0x90903bab, 0x88880b83, +0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, +0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76, +0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, +0x494992db, 0x06060c0a, 0x2424486c, 0x5c5cb8e4, +0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, +0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b, +0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, +0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, +0x6c6cd8b4, 0x5656acfa, 0xf4f4f307, 0xeaeacf25, +0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x08081018, +0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72, +0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, +0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, +0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, +0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa, +0x484890d8, 0x03030605, 0xf6f6f701, 0x0e0e1c12, +0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, +0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, +0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, +0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7, +0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920, +0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, +0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, +0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8, +0x414182c3, 0x999929b0, 0x2d2d5a77, 0x0f0f1e11, +0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a ]; + +var T3 = [ +0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, +0xf2ff0df2, 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, +0x30605030, 0x01020301, 0x67cea967, 0x2b567d2b, +0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76, +0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, +0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, +0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, +0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0, +0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26, +0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, +0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, +0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, +0x04080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3, +0x18302818, 0x9637a196, 0x050a0f05, 0x9a2fb59a, +0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, +0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, +0x09121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a, +0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, +0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, +0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784, +0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, +0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, +0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39, +0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf, +0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, +0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, +0x458acf45, 0xf9e910f9, 0x02040602, 0x7ffe817f, +0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, +0x51a2f351, 0xa35dfea3, 0x4080c040, 0x8f058a8f, +0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, +0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, +0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, +0xcd814ccd, 0x0c18140c, 0x13263513, 0xecc32fec, +0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, +0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, +0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, +0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, +0x22446622, 0x2a547e2a, 0x903bab90, 0x880b8388, +0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, +0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db, +0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, +0x4992db49, 0x060c0a06, 0x24486c24, 0x5cb8e45c, +0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, +0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79, +0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, +0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, +0x6cd8b46c, 0x56acfa56, 0xf4f307f4, 0xeacf25ea, +0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x08101808, +0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e, +0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, +0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, +0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, +0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66, +0x4890d848, 0x03060503, 0xf6f701f6, 0x0e1c120e, +0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, +0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, +0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, +0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794, +0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9, +0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, +0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, +0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868, +0x4182c341, 0x9929b099, 0x2d5a772d, 0x0f1e110f, +0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16 ]; + +var T4 = [ +0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, +0xff0df2f2, 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, +0x60503030, 0x02030101, 0xcea96767, 0x567d2b2b, +0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676, +0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, +0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, +0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, +0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0, +0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, +0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, +0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, +0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, +0x080c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3, +0x30281818, 0x37a19696, 0x0a0f0505, 0x2fb59a9a, +0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, +0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, +0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a, +0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, +0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, +0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484, +0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, +0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, +0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939, +0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf, +0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, +0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, +0x8acf4545, 0xe910f9f9, 0x04060202, 0xfe817f7f, +0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, +0xa2f35151, 0x5dfea3a3, 0x80c04040, 0x058a8f8f, +0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, +0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, +0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, +0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec, +0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, +0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, +0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, +0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, +0x44662222, 0x547e2a2a, 0x3bab9090, 0x0b838888, +0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, +0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, +0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, +0x92db4949, 0x0c0a0606, 0x486c2424, 0xb8e45c5c, +0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, +0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979, +0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, +0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, +0xd8b46c6c, 0xacfa5656, 0xf307f4f4, 0xcf25eaea, +0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, +0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e, +0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, +0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, +0x96dd4b4b, 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, +0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666, +0x90d84848, 0x06050303, 0xf701f6f6, 0x1c120e0e, +0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, +0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, +0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, +0xd2bb6969, 0xa970d9d9, 0x07898e8e, 0x33a79494, +0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9, +0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, +0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, +0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868, +0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f, +0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616 ]; + +function B0(x) { return (x&255); } +function B1(x) { return ((x>>8)&255); } +function B2(x) { return ((x>>16)&255); } +function B3(x) { return ((x>>24)&255); } + +function F1(x0, x1, x2, x3) +{ + return B1(T1[x0&255]) | (B1(T1[(x1>>8)&255])<<8) + | (B1(T1[(x2>>16)&255])<<16) | (B1(T1[x3>>>24])<<24); +} + +function packBytes(octets) +{ + var i, j; + var len=octets.length; + var b=new Array(len/4); + + if (!octets || len % 4) return; + + for (i=0, j=0; j=0; j--) tk[j] = k[j]; + + r=0; + t=0; + for(j=0; (j>8)&255] ^ T3[(t2>>16)&255] ^ T4[t3>>>24]; + b1 = T1[t1&255] ^ T2[(t2>>8)&255] ^ T3[(t3>>16)&255] ^ T4[t0>>>24]; + b2 = T1[t2&255] ^ T2[(t3>>8)&255] ^ T3[(t0>>16)&255] ^ T4[t1>>>24]; + b3 = T1[t3&255] ^ T2[(t0>>8)&255] ^ T3[(t1>>16)&255] ^ T4[t2>>>24]; + } + + // last round is special + r = rounds-1; + + t0 = b0 ^ ctx.rk[r][0]; + t1 = b1 ^ ctx.rk[r][1]; + t2 = b2 ^ ctx.rk[r][2]; + t3 = b3 ^ ctx.rk[r][3]; + + b[0] = F1(t0, t1, t2, t3) ^ ctx.rk[rounds][0]; + b[1] = F1(t1, t2, t3, t0) ^ ctx.rk[rounds][1]; + b[2] = F1(t2, t3, t0, t1) ^ ctx.rk[rounds][2]; + b[3] = F1(t3, t0, t1, t2) ^ ctx.rk[rounds][3]; + + return unpackBytes(b); +} diff --git a/src/ciphers/symmetric/blowfish.js b/src/ciphers/symmetric/blowfish.js new file mode 100755 index 00000000..13b384ac --- /dev/null +++ b/src/ciphers/symmetric/blowfish.js @@ -0,0 +1,393 @@ +/* Modified by Recurity Labs GmbH + * + * Originally written by nklein software (nklein.com) + */ + +/* + * Javascript implementation based on Bruce Schneier's reference implementation. + * + * + * The constructor doesn't do much of anything. It's just here + * so we can start defining properties and methods and such. + */ +function Blowfish() { +}; + +/* + * Declare the block size so that protocols know what size + * Initialization Vector (IV) they will need. + */ +Blowfish.prototype.BLOCKSIZE = 8; + +/* + * These are the default SBOXES. + */ +Blowfish.prototype.SBOXES = [ + [ + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, + 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, + 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658, + 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, + 0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, + 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6, + 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, + 0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, + 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1, + 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, + 0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, + 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176, + 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, + 0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, + 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, + 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, + 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, + 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, + 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, + 0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, + 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8, + 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, + 0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, + 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0, + 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, + 0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, + 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705, + 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, + 0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, + 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, + 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, + 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, + 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a + ], [ + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, + 0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, + 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, + 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, + 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, + 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d, + 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, + 0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, + 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908, + 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, + 0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, + 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908, + 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, + 0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, + 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa, + 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, + 0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, + 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, + 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, + 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, + 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, + 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, + 0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, + 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054, + 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, + 0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, + 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646, + 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, + 0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, + 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e, + 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, + 0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, + 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7 + ], [ + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, + 0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, + 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af, + 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, + 0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, + 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, + 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, + 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, + 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58, + 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, + 0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, + 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60, + 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, + 0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, + 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74, + 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, + 0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, + 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979, + 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, + 0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, + 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, + 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, + 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, + 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, + 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, + 0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, + 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe, + 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, + 0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, + 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188, + 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, + 0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, + 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0 + ], [ + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, + 0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, + 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79, + 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, + 0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, + 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1, + 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, + 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, + 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, + 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, + 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, + 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5, + 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, + 0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, + 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd, + 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, + 0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, + 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc, + 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, + 0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, + 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a, + 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, + 0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, + 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, + 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, + 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, + 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623, + 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, + 0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, + 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3, + 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, + 0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, + 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 + ] +]; + +//* +//* This is the default PARRAY +//* +Blowfish.prototype.PARRAY = [ + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, + 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, + 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b +]; + +//* +//* This is the number of rounds the cipher will go +//* +Blowfish.prototype.NN = 16; + +//* +//* This function is needed to get rid of problems +//* with the high-bit getting set. If we don't do +//* this, then sometimes ( aa & 0x00FFFFFFFF ) is not +//* equal to ( bb & 0x00FFFFFFFF ) even when they +//* agree bit-for-bit for the first 32 bits. +//* +Blowfish.prototype._clean = function( xx ) { + if ( xx < 0 ) { + var yy = xx & 0x7FFFFFFF; + xx = yy + 0x80000000; + } + return xx; +}; + +//* +//* This is the mixing function that uses the sboxes +//* +Blowfish.prototype._F = function ( xx ) { + var aa; + var bb; + var cc; + var dd; + var yy; + + dd = xx & 0x00FF; + xx >>>= 8; + cc = xx & 0x00FF; + xx >>>= 8; + bb = xx & 0x00FF; + xx >>>= 8; + aa = xx & 0x00FF; + + yy = this.sboxes[ 0 ][ aa ] + this.sboxes[ 1 ][ bb ]; + yy = yy ^ this.sboxes[ 2 ][ cc ]; + yy = yy + this.sboxes[ 3 ][ dd ]; + + return yy; +}; + +//* +//* This method takes an array with two values, left and right +//* and does NN rounds of Blowfish on them. +//* +Blowfish.prototype._encrypt_block = function ( vals ) { + var dataL = vals[ 0 ]; + var dataR = vals[ 1 ]; + + var ii; + + for ( ii=0; ii < this.NN; ++ii ) { + dataL = dataL ^ this.parray[ ii ]; + dataR = this._F( dataL ) ^ dataR; + + var tmp = dataL; + dataL = dataR; + dataR = tmp; + } + + dataL = dataL ^ this.parray[ this.NN + 0 ]; + dataR = dataR ^ this.parray[ this.NN + 1 ]; + + vals[ 0 ] = this._clean( dataR ); + vals[ 1 ] = this._clean( dataL ); +}; + +//* +//* This method takes a vector of numbers and turns them +//* into long words so that they can be processed by the +//* real algorithm. +//* +//* Maybe I should make the real algorithm above take a vector +//* instead. That will involve more looping, but it won't require +//* the F() method to deconstruct the vector. +//* +Blowfish.prototype.encrypt_block = function ( vector ) { + var ii; + var vals = [ 0, 0 ]; + var off = this.BLOCKSIZE/2; + for ( ii = 0; ii < this.BLOCKSIZE/2; ++ii ) { + vals[0] = ( vals[0] << 8 ) | ( vector[ ii + 0 ] & 0x00FF ); + vals[1] = ( vals[1] << 8 ) | ( vector[ ii + off ] & 0x00FF ); + } + + this._encrypt_block( vals ); + + var ret = [ ]; + for ( ii = 0; ii < this.BLOCKSIZE/2; ++ii ) { + ret[ ii + 0 ] = ( vals[ 0 ] >>> (24 - 8*(ii)) & 0x00FF ); + ret[ ii + off ] = ( vals[ 1 ] >>> (24 - 8*(ii)) & 0x00FF ); + // vals[ 0 ] = ( vals[ 0 ] >>> 8 ); + // vals[ 1 ] = ( vals[ 1 ] >>> 8 ); + } + + return ret; +}; + +//* +//* This method takes an array with two values, left and right +//* and undoes NN rounds of Blowfish on them. +//* +Blowfish.prototype._decrypt_block = function ( vals ) { + var dataL = vals[ 0 ]; + var dataR = vals[ 1 ]; + + var ii; + + for ( ii=this.NN+1; ii > 1; --ii ) { + dataL = dataL ^ this.parray[ ii ]; + dataR = this._F( dataL ) ^ dataR; + + var tmp = dataL; + dataL = dataR; + dataR = tmp; + } + + dataL = dataL ^ this.parray[ 1 ]; + dataR = dataR ^ this.parray[ 0 ]; + + vals[ 0 ] = this._clean( dataR ); + vals[ 1 ] = this._clean( dataL ); +}; + +//* +//* This method takes a key array and initializes the +//* sboxes and parray for this encryption. +//* +Blowfish.prototype.init = function ( key ) { + var ii; + var jj = 0; + + this.parray = []; + for ( ii=0; ii < this.NN + 2; ++ii ) { + var data = 0x00000000; + var kk; + for ( kk=0; kk < 4; ++kk ) { + data = ( data << 8 ) | ( key[ jj ] & 0x00FF ); + if ( ++jj >= key.length ) { + jj = 0; + } + } + this.parray[ ii ] = this.PARRAY[ ii ] ^ data; + } + + this.sboxes = []; + for ( ii=0; ii < 4; ++ii ) { + this.sboxes[ ii ] = []; + for ( jj=0; jj < 256; ++jj ) { + this.sboxes[ ii ][ jj ] = this.SBOXES[ ii ][ jj ]; + } + } + + var vals = [ 0x00000000, 0x00000000 ]; + + for ( ii=0; ii < this.NN+2; ii += 2 ) { + this._encrypt_block( vals ); + this.parray[ ii + 0 ] = vals[ 0 ]; + this.parray[ ii + 1 ] = vals[ 1 ]; + } + + for ( ii=0; ii < 4; ++ii ) { + for ( jj=0; jj < 256; jj += 2 ) { + this._encrypt_block( vals ); + this.sboxes[ ii ][ jj + 0 ] = vals[ 0 ]; + this.sboxes[ ii ][ jj + 1 ] = vals[ 1 ]; + } + } +}; + +// added by Recurity Labs +function BFencrypt(block,key) { + var bf = new Blowfish(); + bf.init(util.str2bin(key)); + return bf.encrypt_block(block); +} diff --git a/src/ciphers/symmetric/cast5.js b/src/ciphers/symmetric/cast5.js new file mode 100755 index 00000000..a13eb651 --- /dev/null +++ b/src/ciphers/symmetric/cast5.js @@ -0,0 +1,547 @@ + +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Copyright 2010 pjacobs@xeekr.com . All rights reserved. + +// Modified by Recurity Labs GmbH + +// fixed/modified by Herbert Hanewinkel, www.haneWIN.de +// check www.haneWIN.de for the latest version + +// cast5.js is a Javascript implementation of CAST-128, as defined in RFC 2144. +// CAST-128 is a common OpenPGP cipher. + + +// CAST5 constructor + +function cast5_encrypt(block, key) { + var cast5 = new openpgp_symenc_cast5(); + cast5.setKey(util.str2bin(key)); + return cast5.encrypt(block); +} + +function openpgp_symenc_cast5() { + this.BlockSize= 8; + this.KeySize = 16; + + this.setKey = function (key) { + this.masking = new Array(16); + this.rotate = new Array(16); + + this.reset(); + + if (key.length == this.KeySize) + { + this.keySchedule(key); + } + else + { + util.print_error('cast5.js: CAST-128: keys must be 16 bytes'); + return false; + } + return true; + }; + + this.reset = function() { + for (var i = 0; i < 16; i++) + { + this.masking[i] = 0; + this.rotate[i] = 0; + } + }; + + this.getBlockSize = function() { + return BlockSize; + }; + + this.encrypt = function(src) { + var dst = new Array(src.length); + + for(i = 0; i < src.length; i+=8) + { + var l = src[i]<<24 | src[i+1]<<16 | src[i+2]<<8 | src[i+3]; + var r = src[i+4]<<24 | src[i+5]<<16 | src[i+6]<<8 | src[i+7]; + var t; + + t = r; r = l^f1(r, this.masking[0], this.rotate[0]); l = t; + t = r; r = l^f2(r, this.masking[1], this.rotate[1]); l = t; + t = r; r = l^f3(r, this.masking[2], this.rotate[2]); l = t; + t = r; r = l^f1(r, this.masking[3], this.rotate[3]); l = t; + + t = r; r = l^f2(r, this.masking[4], this.rotate[4]); l = t; + t = r; r = l^f3(r, this.masking[5], this.rotate[5]); l = t; + t = r; r = l^f1(r, this.masking[6], this.rotate[6]); l = t; + t = r; r = l^f2(r, this.masking[7], this.rotate[7]); l = t; + + t = r; r = l^f3(r, this.masking[8], this.rotate[8]); l = t; + t = r; r = l^f1(r, this.masking[9], this.rotate[9]); l = t; + t = r; r = l^f2(r, this.masking[10], this.rotate[10]); l = t; + t = r; r = l^f3(r, this.masking[11], this.rotate[11]); l = t; + + t = r; r = l^f1(r, this.masking[12], this.rotate[12]); l = t; + t = r; r = l^f2(r, this.masking[13], this.rotate[13]); l = t; + t = r; r = l^f3(r, this.masking[14], this.rotate[14]); l = t; + t = r; r = l^f1(r, this.masking[15], this.rotate[15]); l = t; + + dst[i] = (r >>> 24)&255; + dst[i+1] = (r >>> 16)&255; + dst[i+2] = (r >>> 8)&255; + dst[i+3] = r&255; + dst[i+4] = (l >>> 24)&255; + dst[i+5] = (l >>> 16)&255; + dst[i+6] = (l >>> 8)&255; + dst[i+7] = l&255; + } + + return dst; + }; + + this.decrypt = function(src) { + var dst = new Array(src.length); + + for(i = 0; i < src.length; i+=8) + { + var l = src[i]<<24 | src[i+1]<<16 | src[i+2]<<8 | src[i+3]; + var r = src[i+4]<<24 | src[i+5]<<16 | src[i+6]<<8 | src[i+7]; + var t; + + t = r; r = l^f1(r, this.masking[15], this.rotate[15]); l = t; + t = r; r = l^f3(r, this.masking[14], this.rotate[14]); l = t; + t = r; r = l^f2(r, this.masking[13], this.rotate[13]); l = t; + t = r; r = l^f1(r, this.masking[12], this.rotate[12]); l = t; + + t = r; r = l^f3(r, this.masking[11], this.rotate[11]); l = t; + t = r; r = l^f2(r, this.masking[10], this.rotate[10]); l = t; + t = r; r = l^f1(r, this.masking[9], this.rotate[9]); l = t; + t = r; r = l^f3(r, this.masking[8], this.rotate[8]); l = t; + + t = r; r = l^f2(r, this.masking[7], this.rotate[7]); l = t; + t = r; r = l^f1(r, this.masking[6], this.rotate[6]); l = t; + t = r; r = l^f3(r, this.masking[5], this.rotate[5]); l = t; + t = r; r = l^f2(r, this.masking[4], this.rotate[4]); l = t; + + t = r; r = l^f1(r, this.masking[3], this.rotate[3]); l = t; + t = r; r = l^f3(r, this.masking[2], this.rotate[2]); l = t; + t = r; r = l^f2(r, this.masking[1], this.rotate[1]); l = t; + t = r; r = l^f1(r, this.masking[0], this.rotate[0]); l = t; + + dst[i] = (r >>> 24)&255; + dst[i+1] = (r >>> 16)&255; + dst[i+2] = (r >>> 8)&255; + dst[i+3] = r&255; + dst[i+4] = (l >>> 24)&255; + dst[i+5] = (l >> 16)&255; + dst[i+6] = (l >> 8)&255; + dst[i+7] = l&255; + } + + return dst; + }; + var scheduleA = new Array(4); + + scheduleA[0] = new Array(4); + scheduleA[0][0] = new Array(4, 0, 0xd, 0xf, 0xc, 0xe, 0x8); + scheduleA[0][1] = new Array(5, 2, 16 + 0, 16 + 2, 16 + 1, 16 + 3, 0xa); + scheduleA[0][2] = new Array(6, 3, 16 + 7, 16 + 6, 16 + 5, 16 + 4, 9); + scheduleA[0][3] = new Array(7, 1, 16 + 0xa, 16 + 9, 16 + 0xb, 16 + 8, 0xb); + + scheduleA[1] = new Array(4); + scheduleA[1][0] = new Array(0, 6, 16 + 5, 16 + 7, 16 + 4, 16 + 6, 16 + 0); + scheduleA[1][1] = new Array(1, 4, 0, 2, 1, 3, 16 + 2); + scheduleA[1][2] = new Array(2, 5, 7, 6, 5, 4, 16 + 1); + scheduleA[1][3] = new Array(3, 7, 0xa, 9, 0xb, 8, 16 + 3); + + scheduleA[2] = new Array(4); + scheduleA[2][0] = new Array(4, 0, 0xd, 0xf, 0xc, 0xe, 8); + scheduleA[2][1] = new Array(5, 2, 16 + 0, 16 + 2, 16 + 1, 16 + 3, 0xa); + scheduleA[2][2] = new Array(6, 3, 16 + 7, 16 + 6, 16 + 5, 16 + 4, 9); + scheduleA[2][3] = new Array(7, 1, 16 + 0xa, 16 + 9, 16 + 0xb, 16 + 8, 0xb); + + + scheduleA[3] = new Array(4); + scheduleA[3][0] = new Array(0, 6, 16 + 5, 16 + 7, 16 + 4, 16 + 6, 16 + 0); + scheduleA[3][1] = new Array(1, 4, 0, 2, 1, 3, 16 + 2); + scheduleA[3][2] = new Array(2, 5, 7, 6, 5, 4, 16 + 1); + scheduleA[3][3] = new Array(3, 7, 0xa, 9, 0xb, 8, 16 + 3); + + var scheduleB = new Array(4); + + scheduleB[0] = new Array(4); + scheduleB[0][0] = new Array(16 + 8, 16 + 9, 16 + 7, 16 + 6, 16 + 2); + scheduleB[0][1] = new Array(16 + 0xa, 16 + 0xb, 16 + 5, 16 + 4, 16 + 6); + scheduleB[0][2] = new Array(16 + 0xc, 16 + 0xd, 16 + 3, 16 + 2, 16 + 9); + scheduleB[0][3] = new Array(16 + 0xe, 16 + 0xf, 16 + 1, 16 + 0, 16 + 0xc); + + scheduleB[1] = new Array(4); + scheduleB[1][0] = new Array(3, 2, 0xc, 0xd, 8); + scheduleB[1][1] = new Array(1, 0, 0xe, 0xf, 0xd); + scheduleB[1][2] = new Array(7, 6, 8, 9, 3); + scheduleB[1][3] = new Array(5, 4, 0xa, 0xb, 7); + + + scheduleB[2] = new Array(4); + scheduleB[2][0] = new Array(16 + 3, 16 + 2, 16 + 0xc, 16 + 0xd, 16 + 9); + scheduleB[2][1] = new Array(16 + 1, 16 + 0, 16 + 0xe, 16 + 0xf, 16 + 0xc); + scheduleB[2][2] = new Array(16 + 7, 16 + 6, 16 + 8, 16 + 9, 16 + 2); + scheduleB[2][3] = new Array(16 + 5, 16 + 4, 16 + 0xa, 16 + 0xb, 16 + 6); + + + scheduleB[3] = new Array(4); + scheduleB[3][0] = new Array(8, 9, 7, 6, 3); + scheduleB[3][1] = new Array(0xa, 0xb, 5, 4, 7); + scheduleB[3][2] = new Array(0xc, 0xd, 3, 2, 8); + scheduleB[3][3] = new Array(0xe, 0xf, 1, 0, 0xd); + + // changed 'in' to 'inn' (in javascript 'in' is a reserved word) + this.keySchedule = function(inn) + { + var t = new Array(8); + var k = new Array(32); + + for (var i = 0; i < 4; i++) + { + var j = i * 4; + t[i] = inn[j]<<24 | inn[j+1]<<16 | inn[j+2]<<8 | inn[j+3]; + } + + var x = [6, 7, 4, 5]; + var ki = 0; + + for (var half = 0; half < 2; half++) + { + for (var round = 0; round < 4; round++) + { + for (var j = 0; j < 4; j++) + { + var a = scheduleA[round][j]; + var w = t[a[1]]; + + w ^= sBox[4][(t[a[2]>>>2]>>>(24-8*(a[2]&3)))&0xff]; + w ^= sBox[5][(t[a[3]>>>2]>>>(24-8*(a[3]&3)))&0xff]; + w ^= sBox[6][(t[a[4]>>>2]>>>(24-8*(a[4]&3)))&0xff]; + w ^= sBox[7][(t[a[5]>>>2]>>>(24-8*(a[5]&3)))&0xff]; + w ^= sBox[x[j]][(t[a[6]>>>2]>>>(24-8*(a[6]&3)))&0xff]; + t[a[0]] = w; + } + + for (var j = 0; j < 4; j++) + { + var b = scheduleB[round][j]; + var w = sBox[4][(t[b[0]>>>2]>>>(24-8*(b[0]&3)))&0xff]; + + w ^= sBox[5][(t[b[1]>>>2]>>>(24-8*(b[1]&3)))&0xff]; + w ^= sBox[6][(t[b[2]>>>2]>>>(24-8*(b[2]&3)))&0xff]; + w ^= sBox[7][(t[b[3]>>>2]>>>(24-8*(b[3]&3)))&0xff]; + w ^= sBox[4+j][(t[b[4]>>>2]>>>(24-8*(b[4]&3)))&0xff]; + k[ki] = w; + ki++; + } + } + } + + for (var i = 0; i < 16; i++) + { + this.masking[i] = k[i]; + this.rotate[i] = k[16+i] & 0x1f; + } + }; + + // These are the three 'f' functions. See RFC 2144, section 2.2. + + function f1(d, m, r) + { + var t = m + d; + var I = (t << r) | (t >>> (32 - r)); + return ((sBox[0][I>>>24] ^ sBox[1][(I>>>16)&255]) - sBox[2][(I>>>8)&255]) + sBox[3][I&255]; + } + + function f2(d, m, r) + { + var t = m ^ d; + var I = (t << r) | (t >>> (32 - r)); + return ((sBox[0][I>>>24] - sBox[1][(I>>>16)&255]) + sBox[2][(I>>>8)&255]) ^ sBox[3][I&255]; + } + + function f3(d, m, r) + { + var t = m - d; + var I = (t << r) | (t >>> (32 - r)); + return ((sBox[0][I>>>24] + sBox[1][(I>>>16)&255]) ^ sBox[2][(I>>>8)&255]) - sBox[3][I&255]; + } + + var sBox = new Array(8); + sBox[0] = new Array( + 0x30fb40d4, 0x9fa0ff0b, 0x6beccd2f, 0x3f258c7a, 0x1e213f2f, 0x9c004dd3, 0x6003e540, 0xcf9fc949, + 0xbfd4af27, 0x88bbbdb5, 0xe2034090, 0x98d09675, 0x6e63a0e0, 0x15c361d2, 0xc2e7661d, 0x22d4ff8e, + 0x28683b6f, 0xc07fd059, 0xff2379c8, 0x775f50e2, 0x43c340d3, 0xdf2f8656, 0x887ca41a, 0xa2d2bd2d, + 0xa1c9e0d6, 0x346c4819, 0x61b76d87, 0x22540f2f, 0x2abe32e1, 0xaa54166b, 0x22568e3a, 0xa2d341d0, + 0x66db40c8, 0xa784392f, 0x004dff2f, 0x2db9d2de, 0x97943fac, 0x4a97c1d8, 0x527644b7, 0xb5f437a7, + 0xb82cbaef, 0xd751d159, 0x6ff7f0ed, 0x5a097a1f, 0x827b68d0, 0x90ecf52e, 0x22b0c054, 0xbc8e5935, + 0x4b6d2f7f, 0x50bb64a2, 0xd2664910, 0xbee5812d, 0xb7332290, 0xe93b159f, 0xb48ee411, 0x4bff345d, + 0xfd45c240, 0xad31973f, 0xc4f6d02e, 0x55fc8165, 0xd5b1caad, 0xa1ac2dae, 0xa2d4b76d, 0xc19b0c50, + 0x882240f2, 0x0c6e4f38, 0xa4e4bfd7, 0x4f5ba272, 0x564c1d2f, 0xc59c5319, 0xb949e354, 0xb04669fe, + 0xb1b6ab8a, 0xc71358dd, 0x6385c545, 0x110f935d, 0x57538ad5, 0x6a390493, 0xe63d37e0, 0x2a54f6b3, + 0x3a787d5f, 0x6276a0b5, 0x19a6fcdf, 0x7a42206a, 0x29f9d4d5, 0xf61b1891, 0xbb72275e, 0xaa508167, + 0x38901091, 0xc6b505eb, 0x84c7cb8c, 0x2ad75a0f, 0x874a1427, 0xa2d1936b, 0x2ad286af, 0xaa56d291, + 0xd7894360, 0x425c750d, 0x93b39e26, 0x187184c9, 0x6c00b32d, 0x73e2bb14, 0xa0bebc3c, 0x54623779, + 0x64459eab, 0x3f328b82, 0x7718cf82, 0x59a2cea6, 0x04ee002e, 0x89fe78e6, 0x3fab0950, 0x325ff6c2, + 0x81383f05, 0x6963c5c8, 0x76cb5ad6, 0xd49974c9, 0xca180dcf, 0x380782d5, 0xc7fa5cf6, 0x8ac31511, + 0x35e79e13, 0x47da91d0, 0xf40f9086, 0xa7e2419e, 0x31366241, 0x051ef495, 0xaa573b04, 0x4a805d8d, + 0x548300d0, 0x00322a3c, 0xbf64cddf, 0xba57a68e, 0x75c6372b, 0x50afd341, 0xa7c13275, 0x915a0bf5, + 0x6b54bfab, 0x2b0b1426, 0xab4cc9d7, 0x449ccd82, 0xf7fbf265, 0xab85c5f3, 0x1b55db94, 0xaad4e324, + 0xcfa4bd3f, 0x2deaa3e2, 0x9e204d02, 0xc8bd25ac, 0xeadf55b3, 0xd5bd9e98, 0xe31231b2, 0x2ad5ad6c, + 0x954329de, 0xadbe4528, 0xd8710f69, 0xaa51c90f, 0xaa786bf6, 0x22513f1e, 0xaa51a79b, 0x2ad344cc, + 0x7b5a41f0, 0xd37cfbad, 0x1b069505, 0x41ece491, 0xb4c332e6, 0x032268d4, 0xc9600acc, 0xce387e6d, + 0xbf6bb16c, 0x6a70fb78, 0x0d03d9c9, 0xd4df39de, 0xe01063da, 0x4736f464, 0x5ad328d8, 0xb347cc96, + 0x75bb0fc3, 0x98511bfb, 0x4ffbcc35, 0xb58bcf6a, 0xe11f0abc, 0xbfc5fe4a, 0xa70aec10, 0xac39570a, + 0x3f04442f, 0x6188b153, 0xe0397a2e, 0x5727cb79, 0x9ceb418f, 0x1cacd68d, 0x2ad37c96, 0x0175cb9d, + 0xc69dff09, 0xc75b65f0, 0xd9db40d8, 0xec0e7779, 0x4744ead4, 0xb11c3274, 0xdd24cb9e, 0x7e1c54bd, + 0xf01144f9, 0xd2240eb1, 0x9675b3fd, 0xa3ac3755, 0xd47c27af, 0x51c85f4d, 0x56907596, 0xa5bb15e6, + 0x580304f0, 0xca042cf1, 0x011a37ea, 0x8dbfaadb, 0x35ba3e4a, 0x3526ffa0, 0xc37b4d09, 0xbc306ed9, + 0x98a52666, 0x5648f725, 0xff5e569d, 0x0ced63d0, 0x7c63b2cf, 0x700b45e1, 0xd5ea50f1, 0x85a92872, + 0xaf1fbda7, 0xd4234870, 0xa7870bf3, 0x2d3b4d79, 0x42e04198, 0x0cd0ede7, 0x26470db8, 0xf881814c, + 0x474d6ad7, 0x7c0c5e5c, 0xd1231959, 0x381b7298, 0xf5d2f4db, 0xab838653, 0x6e2f1e23, 0x83719c9e, + 0xbd91e046, 0x9a56456e, 0xdc39200c, 0x20c8c571, 0x962bda1c, 0xe1e696ff, 0xb141ab08, 0x7cca89b9, + 0x1a69e783, 0x02cc4843, 0xa2f7c579, 0x429ef47d, 0x427b169c, 0x5ac9f049, 0xdd8f0f00, 0x5c8165bf); + + sBox[1] = new Array( + 0x1f201094, 0xef0ba75b, 0x69e3cf7e, 0x393f4380, 0xfe61cf7a, 0xeec5207a, 0x55889c94, 0x72fc0651, + 0xada7ef79, 0x4e1d7235, 0xd55a63ce, 0xde0436ba, 0x99c430ef, 0x5f0c0794, 0x18dcdb7d, 0xa1d6eff3, + 0xa0b52f7b, 0x59e83605, 0xee15b094, 0xe9ffd909, 0xdc440086, 0xef944459, 0xba83ccb3, 0xe0c3cdfb, + 0xd1da4181, 0x3b092ab1, 0xf997f1c1, 0xa5e6cf7b, 0x01420ddb, 0xe4e7ef5b, 0x25a1ff41, 0xe180f806, + 0x1fc41080, 0x179bee7a, 0xd37ac6a9, 0xfe5830a4, 0x98de8b7f, 0x77e83f4e, 0x79929269, 0x24fa9f7b, + 0xe113c85b, 0xacc40083, 0xd7503525, 0xf7ea615f, 0x62143154, 0x0d554b63, 0x5d681121, 0xc866c359, + 0x3d63cf73, 0xcee234c0, 0xd4d87e87, 0x5c672b21, 0x071f6181, 0x39f7627f, 0x361e3084, 0xe4eb573b, + 0x602f64a4, 0xd63acd9c, 0x1bbc4635, 0x9e81032d, 0x2701f50c, 0x99847ab4, 0xa0e3df79, 0xba6cf38c, + 0x10843094, 0x2537a95e, 0xf46f6ffe, 0xa1ff3b1f, 0x208cfb6a, 0x8f458c74, 0xd9e0a227, 0x4ec73a34, + 0xfc884f69, 0x3e4de8df, 0xef0e0088, 0x3559648d, 0x8a45388c, 0x1d804366, 0x721d9bfd, 0xa58684bb, + 0xe8256333, 0x844e8212, 0x128d8098, 0xfed33fb4, 0xce280ae1, 0x27e19ba5, 0xd5a6c252, 0xe49754bd, + 0xc5d655dd, 0xeb667064, 0x77840b4d, 0xa1b6a801, 0x84db26a9, 0xe0b56714, 0x21f043b7, 0xe5d05860, + 0x54f03084, 0x066ff472, 0xa31aa153, 0xdadc4755, 0xb5625dbf, 0x68561be6, 0x83ca6b94, 0x2d6ed23b, + 0xeccf01db, 0xa6d3d0ba, 0xb6803d5c, 0xaf77a709, 0x33b4a34c, 0x397bc8d6, 0x5ee22b95, 0x5f0e5304, + 0x81ed6f61, 0x20e74364, 0xb45e1378, 0xde18639b, 0x881ca122, 0xb96726d1, 0x8049a7e8, 0x22b7da7b, + 0x5e552d25, 0x5272d237, 0x79d2951c, 0xc60d894c, 0x488cb402, 0x1ba4fe5b, 0xa4b09f6b, 0x1ca815cf, + 0xa20c3005, 0x8871df63, 0xb9de2fcb, 0x0cc6c9e9, 0x0beeff53, 0xe3214517, 0xb4542835, 0x9f63293c, + 0xee41e729, 0x6e1d2d7c, 0x50045286, 0x1e6685f3, 0xf33401c6, 0x30a22c95, 0x31a70850, 0x60930f13, + 0x73f98417, 0xa1269859, 0xec645c44, 0x52c877a9, 0xcdff33a6, 0xa02b1741, 0x7cbad9a2, 0x2180036f, + 0x50d99c08, 0xcb3f4861, 0xc26bd765, 0x64a3f6ab, 0x80342676, 0x25a75e7b, 0xe4e6d1fc, 0x20c710e6, + 0xcdf0b680, 0x17844d3b, 0x31eef84d, 0x7e0824e4, 0x2ccb49eb, 0x846a3bae, 0x8ff77888, 0xee5d60f6, + 0x7af75673, 0x2fdd5cdb, 0xa11631c1, 0x30f66f43, 0xb3faec54, 0x157fd7fa, 0xef8579cc, 0xd152de58, + 0xdb2ffd5e, 0x8f32ce19, 0x306af97a, 0x02f03ef8, 0x99319ad5, 0xc242fa0f, 0xa7e3ebb0, 0xc68e4906, + 0xb8da230c, 0x80823028, 0xdcdef3c8, 0xd35fb171, 0x088a1bc8, 0xbec0c560, 0x61a3c9e8, 0xbca8f54d, + 0xc72feffa, 0x22822e99, 0x82c570b4, 0xd8d94e89, 0x8b1c34bc, 0x301e16e6, 0x273be979, 0xb0ffeaa6, + 0x61d9b8c6, 0x00b24869, 0xb7ffce3f, 0x08dc283b, 0x43daf65a, 0xf7e19798, 0x7619b72f, 0x8f1c9ba4, + 0xdc8637a0, 0x16a7d3b1, 0x9fc393b7, 0xa7136eeb, 0xc6bcc63e, 0x1a513742, 0xef6828bc, 0x520365d6, + 0x2d6a77ab, 0x3527ed4b, 0x821fd216, 0x095c6e2e, 0xdb92f2fb, 0x5eea29cb, 0x145892f5, 0x91584f7f, + 0x5483697b, 0x2667a8cc, 0x85196048, 0x8c4bacea, 0x833860d4, 0x0d23e0f9, 0x6c387e8a, 0x0ae6d249, + 0xb284600c, 0xd835731d, 0xdcb1c647, 0xac4c56ea, 0x3ebd81b3, 0x230eabb0, 0x6438bc87, 0xf0b5b1fa, + 0x8f5ea2b3, 0xfc184642, 0x0a036b7a, 0x4fb089bd, 0x649da589, 0xa345415e, 0x5c038323, 0x3e5d3bb9, + 0x43d79572, 0x7e6dd07c, 0x06dfdf1e, 0x6c6cc4ef, 0x7160a539, 0x73bfbe70, 0x83877605, 0x4523ecf1); + + sBox[2] = new Array( + 0x8defc240, 0x25fa5d9f, 0xeb903dbf, 0xe810c907, 0x47607fff, 0x369fe44b, 0x8c1fc644, 0xaececa90, + 0xbeb1f9bf, 0xeefbcaea, 0xe8cf1950, 0x51df07ae, 0x920e8806, 0xf0ad0548, 0xe13c8d83, 0x927010d5, + 0x11107d9f, 0x07647db9, 0xb2e3e4d4, 0x3d4f285e, 0xb9afa820, 0xfade82e0, 0xa067268b, 0x8272792e, + 0x553fb2c0, 0x489ae22b, 0xd4ef9794, 0x125e3fbc, 0x21fffcee, 0x825b1bfd, 0x9255c5ed, 0x1257a240, + 0x4e1a8302, 0xbae07fff, 0x528246e7, 0x8e57140e, 0x3373f7bf, 0x8c9f8188, 0xa6fc4ee8, 0xc982b5a5, + 0xa8c01db7, 0x579fc264, 0x67094f31, 0xf2bd3f5f, 0x40fff7c1, 0x1fb78dfc, 0x8e6bd2c1, 0x437be59b, + 0x99b03dbf, 0xb5dbc64b, 0x638dc0e6, 0x55819d99, 0xa197c81c, 0x4a012d6e, 0xc5884a28, 0xccc36f71, + 0xb843c213, 0x6c0743f1, 0x8309893c, 0x0feddd5f, 0x2f7fe850, 0xd7c07f7e, 0x02507fbf, 0x5afb9a04, + 0xa747d2d0, 0x1651192e, 0xaf70bf3e, 0x58c31380, 0x5f98302e, 0x727cc3c4, 0x0a0fb402, 0x0f7fef82, + 0x8c96fdad, 0x5d2c2aae, 0x8ee99a49, 0x50da88b8, 0x8427f4a0, 0x1eac5790, 0x796fb449, 0x8252dc15, + 0xefbd7d9b, 0xa672597d, 0xada840d8, 0x45f54504, 0xfa5d7403, 0xe83ec305, 0x4f91751a, 0x925669c2, + 0x23efe941, 0xa903f12e, 0x60270df2, 0x0276e4b6, 0x94fd6574, 0x927985b2, 0x8276dbcb, 0x02778176, + 0xf8af918d, 0x4e48f79e, 0x8f616ddf, 0xe29d840e, 0x842f7d83, 0x340ce5c8, 0x96bbb682, 0x93b4b148, + 0xef303cab, 0x984faf28, 0x779faf9b, 0x92dc560d, 0x224d1e20, 0x8437aa88, 0x7d29dc96, 0x2756d3dc, + 0x8b907cee, 0xb51fd240, 0xe7c07ce3, 0xe566b4a1, 0xc3e9615e, 0x3cf8209d, 0x6094d1e3, 0xcd9ca341, + 0x5c76460e, 0x00ea983b, 0xd4d67881, 0xfd47572c, 0xf76cedd9, 0xbda8229c, 0x127dadaa, 0x438a074e, + 0x1f97c090, 0x081bdb8a, 0x93a07ebe, 0xb938ca15, 0x97b03cff, 0x3dc2c0f8, 0x8d1ab2ec, 0x64380e51, + 0x68cc7bfb, 0xd90f2788, 0x12490181, 0x5de5ffd4, 0xdd7ef86a, 0x76a2e214, 0xb9a40368, 0x925d958f, + 0x4b39fffa, 0xba39aee9, 0xa4ffd30b, 0xfaf7933b, 0x6d498623, 0x193cbcfa, 0x27627545, 0x825cf47a, + 0x61bd8ba0, 0xd11e42d1, 0xcead04f4, 0x127ea392, 0x10428db7, 0x8272a972, 0x9270c4a8, 0x127de50b, + 0x285ba1c8, 0x3c62f44f, 0x35c0eaa5, 0xe805d231, 0x428929fb, 0xb4fcdf82, 0x4fb66a53, 0x0e7dc15b, + 0x1f081fab, 0x108618ae, 0xfcfd086d, 0xf9ff2889, 0x694bcc11, 0x236a5cae, 0x12deca4d, 0x2c3f8cc5, + 0xd2d02dfe, 0xf8ef5896, 0xe4cf52da, 0x95155b67, 0x494a488c, 0xb9b6a80c, 0x5c8f82bc, 0x89d36b45, + 0x3a609437, 0xec00c9a9, 0x44715253, 0x0a874b49, 0xd773bc40, 0x7c34671c, 0x02717ef6, 0x4feb5536, + 0xa2d02fff, 0xd2bf60c4, 0xd43f03c0, 0x50b4ef6d, 0x07478cd1, 0x006e1888, 0xa2e53f55, 0xb9e6d4bc, + 0xa2048016, 0x97573833, 0xd7207d67, 0xde0f8f3d, 0x72f87b33, 0xabcc4f33, 0x7688c55d, 0x7b00a6b0, + 0x947b0001, 0x570075d2, 0xf9bb88f8, 0x8942019e, 0x4264a5ff, 0x856302e0, 0x72dbd92b, 0xee971b69, + 0x6ea22fde, 0x5f08ae2b, 0xaf7a616d, 0xe5c98767, 0xcf1febd2, 0x61efc8c2, 0xf1ac2571, 0xcc8239c2, + 0x67214cb8, 0xb1e583d1, 0xb7dc3e62, 0x7f10bdce, 0xf90a5c38, 0x0ff0443d, 0x606e6dc6, 0x60543a49, + 0x5727c148, 0x2be98a1d, 0x8ab41738, 0x20e1be24, 0xaf96da0f, 0x68458425, 0x99833be5, 0x600d457d, + 0x282f9350, 0x8334b362, 0xd91d1120, 0x2b6d8da0, 0x642b1e31, 0x9c305a00, 0x52bce688, 0x1b03588a, + 0xf7baefd5, 0x4142ed9c, 0xa4315c11, 0x83323ec5, 0xdfef4636, 0xa133c501, 0xe9d3531c, 0xee353783); + + sBox[3] = new Array( + 0x9db30420, 0x1fb6e9de, 0xa7be7bef, 0xd273a298, 0x4a4f7bdb, 0x64ad8c57, 0x85510443, 0xfa020ed1, + 0x7e287aff, 0xe60fb663, 0x095f35a1, 0x79ebf120, 0xfd059d43, 0x6497b7b1, 0xf3641f63, 0x241e4adf, + 0x28147f5f, 0x4fa2b8cd, 0xc9430040, 0x0cc32220, 0xfdd30b30, 0xc0a5374f, 0x1d2d00d9, 0x24147b15, + 0xee4d111a, 0x0fca5167, 0x71ff904c, 0x2d195ffe, 0x1a05645f, 0x0c13fefe, 0x081b08ca, 0x05170121, + 0x80530100, 0xe83e5efe, 0xac9af4f8, 0x7fe72701, 0xd2b8ee5f, 0x06df4261, 0xbb9e9b8a, 0x7293ea25, + 0xce84ffdf, 0xf5718801, 0x3dd64b04, 0xa26f263b, 0x7ed48400, 0x547eebe6, 0x446d4ca0, 0x6cf3d6f5, + 0x2649abdf, 0xaea0c7f5, 0x36338cc1, 0x503f7e93, 0xd3772061, 0x11b638e1, 0x72500e03, 0xf80eb2bb, + 0xabe0502e, 0xec8d77de, 0x57971e81, 0xe14f6746, 0xc9335400, 0x6920318f, 0x081dbb99, 0xffc304a5, + 0x4d351805, 0x7f3d5ce3, 0xa6c866c6, 0x5d5bcca9, 0xdaec6fea, 0x9f926f91, 0x9f46222f, 0x3991467d, + 0xa5bf6d8e, 0x1143c44f, 0x43958302, 0xd0214eeb, 0x022083b8, 0x3fb6180c, 0x18f8931e, 0x281658e6, + 0x26486e3e, 0x8bd78a70, 0x7477e4c1, 0xb506e07c, 0xf32d0a25, 0x79098b02, 0xe4eabb81, 0x28123b23, + 0x69dead38, 0x1574ca16, 0xdf871b62, 0x211c40b7, 0xa51a9ef9, 0x0014377b, 0x041e8ac8, 0x09114003, + 0xbd59e4d2, 0xe3d156d5, 0x4fe876d5, 0x2f91a340, 0x557be8de, 0x00eae4a7, 0x0ce5c2ec, 0x4db4bba6, + 0xe756bdff, 0xdd3369ac, 0xec17b035, 0x06572327, 0x99afc8b0, 0x56c8c391, 0x6b65811c, 0x5e146119, + 0x6e85cb75, 0xbe07c002, 0xc2325577, 0x893ff4ec, 0x5bbfc92d, 0xd0ec3b25, 0xb7801ab7, 0x8d6d3b24, + 0x20c763ef, 0xc366a5fc, 0x9c382880, 0x0ace3205, 0xaac9548a, 0xeca1d7c7, 0x041afa32, 0x1d16625a, + 0x6701902c, 0x9b757a54, 0x31d477f7, 0x9126b031, 0x36cc6fdb, 0xc70b8b46, 0xd9e66a48, 0x56e55a79, + 0x026a4ceb, 0x52437eff, 0x2f8f76b4, 0x0df980a5, 0x8674cde3, 0xedda04eb, 0x17a9be04, 0x2c18f4df, + 0xb7747f9d, 0xab2af7b4, 0xefc34d20, 0x2e096b7c, 0x1741a254, 0xe5b6a035, 0x213d42f6, 0x2c1c7c26, + 0x61c2f50f, 0x6552daf9, 0xd2c231f8, 0x25130f69, 0xd8167fa2, 0x0418f2c8, 0x001a96a6, 0x0d1526ab, + 0x63315c21, 0x5e0a72ec, 0x49bafefd, 0x187908d9, 0x8d0dbd86, 0x311170a7, 0x3e9b640c, 0xcc3e10d7, + 0xd5cad3b6, 0x0caec388, 0xf73001e1, 0x6c728aff, 0x71eae2a1, 0x1f9af36e, 0xcfcbd12f, 0xc1de8417, + 0xac07be6b, 0xcb44a1d8, 0x8b9b0f56, 0x013988c3, 0xb1c52fca, 0xb4be31cd, 0xd8782806, 0x12a3a4e2, + 0x6f7de532, 0x58fd7eb6, 0xd01ee900, 0x24adffc2, 0xf4990fc5, 0x9711aac5, 0x001d7b95, 0x82e5e7d2, + 0x109873f6, 0x00613096, 0xc32d9521, 0xada121ff, 0x29908415, 0x7fbb977f, 0xaf9eb3db, 0x29c9ed2a, + 0x5ce2a465, 0xa730f32c, 0xd0aa3fe8, 0x8a5cc091, 0xd49e2ce7, 0x0ce454a9, 0xd60acd86, 0x015f1919, + 0x77079103, 0xdea03af6, 0x78a8565e, 0xdee356df, 0x21f05cbe, 0x8b75e387, 0xb3c50651, 0xb8a5c3ef, + 0xd8eeb6d2, 0xe523be77, 0xc2154529, 0x2f69efdf, 0xafe67afb, 0xf470c4b2, 0xf3e0eb5b, 0xd6cc9876, + 0x39e4460c, 0x1fda8538, 0x1987832f, 0xca007367, 0xa99144f8, 0x296b299e, 0x492fc295, 0x9266beab, + 0xb5676e69, 0x9bd3ddda, 0xdf7e052f, 0xdb25701c, 0x1b5e51ee, 0xf65324e6, 0x6afce36c, 0x0316cc04, + 0x8644213e, 0xb7dc59d0, 0x7965291f, 0xccd6fd43, 0x41823979, 0x932bcdf6, 0xb657c34d, 0x4edfd282, + 0x7ae5290c, 0x3cb9536b, 0x851e20fe, 0x9833557e, 0x13ecf0b0, 0xd3ffb372, 0x3f85c5c1, 0x0aef7ed2); + + sBox[4] = new Array( + 0x7ec90c04, 0x2c6e74b9, 0x9b0e66df, 0xa6337911, 0xb86a7fff, 0x1dd358f5, 0x44dd9d44, 0x1731167f, + 0x08fbf1fa, 0xe7f511cc, 0xd2051b00, 0x735aba00, 0x2ab722d8, 0x386381cb, 0xacf6243a, 0x69befd7a, + 0xe6a2e77f, 0xf0c720cd, 0xc4494816, 0xccf5c180, 0x38851640, 0x15b0a848, 0xe68b18cb, 0x4caadeff, + 0x5f480a01, 0x0412b2aa, 0x259814fc, 0x41d0efe2, 0x4e40b48d, 0x248eb6fb, 0x8dba1cfe, 0x41a99b02, + 0x1a550a04, 0xba8f65cb, 0x7251f4e7, 0x95a51725, 0xc106ecd7, 0x97a5980a, 0xc539b9aa, 0x4d79fe6a, + 0xf2f3f763, 0x68af8040, 0xed0c9e56, 0x11b4958b, 0xe1eb5a88, 0x8709e6b0, 0xd7e07156, 0x4e29fea7, + 0x6366e52d, 0x02d1c000, 0xc4ac8e05, 0x9377f571, 0x0c05372a, 0x578535f2, 0x2261be02, 0xd642a0c9, + 0xdf13a280, 0x74b55bd2, 0x682199c0, 0xd421e5ec, 0x53fb3ce8, 0xc8adedb3, 0x28a87fc9, 0x3d959981, + 0x5c1ff900, 0xfe38d399, 0x0c4eff0b, 0x062407ea, 0xaa2f4fb1, 0x4fb96976, 0x90c79505, 0xb0a8a774, + 0xef55a1ff, 0xe59ca2c2, 0xa6b62d27, 0xe66a4263, 0xdf65001f, 0x0ec50966, 0xdfdd55bc, 0x29de0655, + 0x911e739a, 0x17af8975, 0x32c7911c, 0x89f89468, 0x0d01e980, 0x524755f4, 0x03b63cc9, 0x0cc844b2, + 0xbcf3f0aa, 0x87ac36e9, 0xe53a7426, 0x01b3d82b, 0x1a9e7449, 0x64ee2d7e, 0xcddbb1da, 0x01c94910, + 0xb868bf80, 0x0d26f3fd, 0x9342ede7, 0x04a5c284, 0x636737b6, 0x50f5b616, 0xf24766e3, 0x8eca36c1, + 0x136e05db, 0xfef18391, 0xfb887a37, 0xd6e7f7d4, 0xc7fb7dc9, 0x3063fcdf, 0xb6f589de, 0xec2941da, + 0x26e46695, 0xb7566419, 0xf654efc5, 0xd08d58b7, 0x48925401, 0xc1bacb7f, 0xe5ff550f, 0xb6083049, + 0x5bb5d0e8, 0x87d72e5a, 0xab6a6ee1, 0x223a66ce, 0xc62bf3cd, 0x9e0885f9, 0x68cb3e47, 0x086c010f, + 0xa21de820, 0xd18b69de, 0xf3f65777, 0xfa02c3f6, 0x407edac3, 0xcbb3d550, 0x1793084d, 0xb0d70eba, + 0x0ab378d5, 0xd951fb0c, 0xded7da56, 0x4124bbe4, 0x94ca0b56, 0x0f5755d1, 0xe0e1e56e, 0x6184b5be, + 0x580a249f, 0x94f74bc0, 0xe327888e, 0x9f7b5561, 0xc3dc0280, 0x05687715, 0x646c6bd7, 0x44904db3, + 0x66b4f0a3, 0xc0f1648a, 0x697ed5af, 0x49e92ff6, 0x309e374f, 0x2cb6356a, 0x85808573, 0x4991f840, + 0x76f0ae02, 0x083be84d, 0x28421c9a, 0x44489406, 0x736e4cb8, 0xc1092910, 0x8bc95fc6, 0x7d869cf4, + 0x134f616f, 0x2e77118d, 0xb31b2be1, 0xaa90b472, 0x3ca5d717, 0x7d161bba, 0x9cad9010, 0xaf462ba2, + 0x9fe459d2, 0x45d34559, 0xd9f2da13, 0xdbc65487, 0xf3e4f94e, 0x176d486f, 0x097c13ea, 0x631da5c7, + 0x445f7382, 0x175683f4, 0xcdc66a97, 0x70be0288, 0xb3cdcf72, 0x6e5dd2f3, 0x20936079, 0x459b80a5, + 0xbe60e2db, 0xa9c23101, 0xeba5315c, 0x224e42f2, 0x1c5c1572, 0xf6721b2c, 0x1ad2fff3, 0x8c25404e, + 0x324ed72f, 0x4067b7fd, 0x0523138e, 0x5ca3bc78, 0xdc0fd66e, 0x75922283, 0x784d6b17, 0x58ebb16e, + 0x44094f85, 0x3f481d87, 0xfcfeae7b, 0x77b5ff76, 0x8c2302bf, 0xaaf47556, 0x5f46b02a, 0x2b092801, + 0x3d38f5f7, 0x0ca81f36, 0x52af4a8a, 0x66d5e7c0, 0xdf3b0874, 0x95055110, 0x1b5ad7a8, 0xf61ed5ad, + 0x6cf6e479, 0x20758184, 0xd0cefa65, 0x88f7be58, 0x4a046826, 0x0ff6f8f3, 0xa09c7f70, 0x5346aba0, + 0x5ce96c28, 0xe176eda3, 0x6bac307f, 0x376829d2, 0x85360fa9, 0x17e3fe2a, 0x24b79767, 0xf5a96b20, + 0xd6cd2595, 0x68ff1ebf, 0x7555442c, 0xf19f06be, 0xf9e0659a, 0xeeb9491d, 0x34010718, 0xbb30cab8, + 0xe822fe15, 0x88570983, 0x750e6249, 0xda627e55, 0x5e76ffa8, 0xb1534546, 0x6d47de08, 0xefe9e7d4); + + sBox[5] = new Array( + 0xf6fa8f9d, 0x2cac6ce1, 0x4ca34867, 0xe2337f7c, 0x95db08e7, 0x016843b4, 0xeced5cbc, 0x325553ac, + 0xbf9f0960, 0xdfa1e2ed, 0x83f0579d, 0x63ed86b9, 0x1ab6a6b8, 0xde5ebe39, 0xf38ff732, 0x8989b138, + 0x33f14961, 0xc01937bd, 0xf506c6da, 0xe4625e7e, 0xa308ea99, 0x4e23e33c, 0x79cbd7cc, 0x48a14367, + 0xa3149619, 0xfec94bd5, 0xa114174a, 0xeaa01866, 0xa084db2d, 0x09a8486f, 0xa888614a, 0x2900af98, + 0x01665991, 0xe1992863, 0xc8f30c60, 0x2e78ef3c, 0xd0d51932, 0xcf0fec14, 0xf7ca07d2, 0xd0a82072, + 0xfd41197e, 0x9305a6b0, 0xe86be3da, 0x74bed3cd, 0x372da53c, 0x4c7f4448, 0xdab5d440, 0x6dba0ec3, + 0x083919a7, 0x9fbaeed9, 0x49dbcfb0, 0x4e670c53, 0x5c3d9c01, 0x64bdb941, 0x2c0e636a, 0xba7dd9cd, + 0xea6f7388, 0xe70bc762, 0x35f29adb, 0x5c4cdd8d, 0xf0d48d8c, 0xb88153e2, 0x08a19866, 0x1ae2eac8, + 0x284caf89, 0xaa928223, 0x9334be53, 0x3b3a21bf, 0x16434be3, 0x9aea3906, 0xefe8c36e, 0xf890cdd9, + 0x80226dae, 0xc340a4a3, 0xdf7e9c09, 0xa694a807, 0x5b7c5ecc, 0x221db3a6, 0x9a69a02f, 0x68818a54, + 0xceb2296f, 0x53c0843a, 0xfe893655, 0x25bfe68a, 0xb4628abc, 0xcf222ebf, 0x25ac6f48, 0xa9a99387, + 0x53bddb65, 0xe76ffbe7, 0xe967fd78, 0x0ba93563, 0x8e342bc1, 0xe8a11be9, 0x4980740d, 0xc8087dfc, + 0x8de4bf99, 0xa11101a0, 0x7fd37975, 0xda5a26c0, 0xe81f994f, 0x9528cd89, 0xfd339fed, 0xb87834bf, + 0x5f04456d, 0x22258698, 0xc9c4c83b, 0x2dc156be, 0x4f628daa, 0x57f55ec5, 0xe2220abe, 0xd2916ebf, + 0x4ec75b95, 0x24f2c3c0, 0x42d15d99, 0xcd0d7fa0, 0x7b6e27ff, 0xa8dc8af0, 0x7345c106, 0xf41e232f, + 0x35162386, 0xe6ea8926, 0x3333b094, 0x157ec6f2, 0x372b74af, 0x692573e4, 0xe9a9d848, 0xf3160289, + 0x3a62ef1d, 0xa787e238, 0xf3a5f676, 0x74364853, 0x20951063, 0x4576698d, 0xb6fad407, 0x592af950, + 0x36f73523, 0x4cfb6e87, 0x7da4cec0, 0x6c152daa, 0xcb0396a8, 0xc50dfe5d, 0xfcd707ab, 0x0921c42f, + 0x89dff0bb, 0x5fe2be78, 0x448f4f33, 0x754613c9, 0x2b05d08d, 0x48b9d585, 0xdc049441, 0xc8098f9b, + 0x7dede786, 0xc39a3373, 0x42410005, 0x6a091751, 0x0ef3c8a6, 0x890072d6, 0x28207682, 0xa9a9f7be, + 0xbf32679d, 0xd45b5b75, 0xb353fd00, 0xcbb0e358, 0x830f220a, 0x1f8fb214, 0xd372cf08, 0xcc3c4a13, + 0x8cf63166, 0x061c87be, 0x88c98f88, 0x6062e397, 0x47cf8e7a, 0xb6c85283, 0x3cc2acfb, 0x3fc06976, + 0x4e8f0252, 0x64d8314d, 0xda3870e3, 0x1e665459, 0xc10908f0, 0x513021a5, 0x6c5b68b7, 0x822f8aa0, + 0x3007cd3e, 0x74719eef, 0xdc872681, 0x073340d4, 0x7e432fd9, 0x0c5ec241, 0x8809286c, 0xf592d891, + 0x08a930f6, 0x957ef305, 0xb7fbffbd, 0xc266e96f, 0x6fe4ac98, 0xb173ecc0, 0xbc60b42a, 0x953498da, + 0xfba1ae12, 0x2d4bd736, 0x0f25faab, 0xa4f3fceb, 0xe2969123, 0x257f0c3d, 0x9348af49, 0x361400bc, + 0xe8816f4a, 0x3814f200, 0xa3f94043, 0x9c7a54c2, 0xbc704f57, 0xda41e7f9, 0xc25ad33a, 0x54f4a084, + 0xb17f5505, 0x59357cbe, 0xedbd15c8, 0x7f97c5ab, 0xba5ac7b5, 0xb6f6deaf, 0x3a479c3a, 0x5302da25, + 0x653d7e6a, 0x54268d49, 0x51a477ea, 0x5017d55b, 0xd7d25d88, 0x44136c76, 0x0404a8c8, 0xb8e5a121, + 0xb81a928a, 0x60ed5869, 0x97c55b96, 0xeaec991b, 0x29935913, 0x01fdb7f1, 0x088e8dfa, 0x9ab6f6f5, + 0x3b4cbf9f, 0x4a5de3ab, 0xe6051d35, 0xa0e1d855, 0xd36b4cf1, 0xf544edeb, 0xb0e93524, 0xbebb8fbd, + 0xa2d762cf, 0x49c92f54, 0x38b5f331, 0x7128a454, 0x48392905, 0xa65b1db8, 0x851c97bd, 0xd675cf2f); + + sBox[6] = new Array( + 0x85e04019, 0x332bf567, 0x662dbfff, 0xcfc65693, 0x2a8d7f6f, 0xab9bc912, 0xde6008a1, 0x2028da1f, + 0x0227bce7, 0x4d642916, 0x18fac300, 0x50f18b82, 0x2cb2cb11, 0xb232e75c, 0x4b3695f2, 0xb28707de, + 0xa05fbcf6, 0xcd4181e9, 0xe150210c, 0xe24ef1bd, 0xb168c381, 0xfde4e789, 0x5c79b0d8, 0x1e8bfd43, + 0x4d495001, 0x38be4341, 0x913cee1d, 0x92a79c3f, 0x089766be, 0xbaeeadf4, 0x1286becf, 0xb6eacb19, + 0x2660c200, 0x7565bde4, 0x64241f7a, 0x8248dca9, 0xc3b3ad66, 0x28136086, 0x0bd8dfa8, 0x356d1cf2, + 0x107789be, 0xb3b2e9ce, 0x0502aa8f, 0x0bc0351e, 0x166bf52a, 0xeb12ff82, 0xe3486911, 0xd34d7516, + 0x4e7b3aff, 0x5f43671b, 0x9cf6e037, 0x4981ac83, 0x334266ce, 0x8c9341b7, 0xd0d854c0, 0xcb3a6c88, + 0x47bc2829, 0x4725ba37, 0xa66ad22b, 0x7ad61f1e, 0x0c5cbafa, 0x4437f107, 0xb6e79962, 0x42d2d816, + 0x0a961288, 0xe1a5c06e, 0x13749e67, 0x72fc081a, 0xb1d139f7, 0xf9583745, 0xcf19df58, 0xbec3f756, + 0xc06eba30, 0x07211b24, 0x45c28829, 0xc95e317f, 0xbc8ec511, 0x38bc46e9, 0xc6e6fa14, 0xbae8584a, + 0xad4ebc46, 0x468f508b, 0x7829435f, 0xf124183b, 0x821dba9f, 0xaff60ff4, 0xea2c4e6d, 0x16e39264, + 0x92544a8b, 0x009b4fc3, 0xaba68ced, 0x9ac96f78, 0x06a5b79a, 0xb2856e6e, 0x1aec3ca9, 0xbe838688, + 0x0e0804e9, 0x55f1be56, 0xe7e5363b, 0xb3a1f25d, 0xf7debb85, 0x61fe033c, 0x16746233, 0x3c034c28, + 0xda6d0c74, 0x79aac56c, 0x3ce4e1ad, 0x51f0c802, 0x98f8f35a, 0x1626a49f, 0xeed82b29, 0x1d382fe3, + 0x0c4fb99a, 0xbb325778, 0x3ec6d97b, 0x6e77a6a9, 0xcb658b5c, 0xd45230c7, 0x2bd1408b, 0x60c03eb7, + 0xb9068d78, 0xa33754f4, 0xf430c87d, 0xc8a71302, 0xb96d8c32, 0xebd4e7be, 0xbe8b9d2d, 0x7979fb06, + 0xe7225308, 0x8b75cf77, 0x11ef8da4, 0xe083c858, 0x8d6b786f, 0x5a6317a6, 0xfa5cf7a0, 0x5dda0033, + 0xf28ebfb0, 0xf5b9c310, 0xa0eac280, 0x08b9767a, 0xa3d9d2b0, 0x79d34217, 0x021a718d, 0x9ac6336a, + 0x2711fd60, 0x438050e3, 0x069908a8, 0x3d7fedc4, 0x826d2bef, 0x4eeb8476, 0x488dcf25, 0x36c9d566, + 0x28e74e41, 0xc2610aca, 0x3d49a9cf, 0xbae3b9df, 0xb65f8de6, 0x92aeaf64, 0x3ac7d5e6, 0x9ea80509, + 0xf22b017d, 0xa4173f70, 0xdd1e16c3, 0x15e0d7f9, 0x50b1b887, 0x2b9f4fd5, 0x625aba82, 0x6a017962, + 0x2ec01b9c, 0x15488aa9, 0xd716e740, 0x40055a2c, 0x93d29a22, 0xe32dbf9a, 0x058745b9, 0x3453dc1e, + 0xd699296e, 0x496cff6f, 0x1c9f4986, 0xdfe2ed07, 0xb87242d1, 0x19de7eae, 0x053e561a, 0x15ad6f8c, + 0x66626c1c, 0x7154c24c, 0xea082b2a, 0x93eb2939, 0x17dcb0f0, 0x58d4f2ae, 0x9ea294fb, 0x52cf564c, + 0x9883fe66, 0x2ec40581, 0x763953c3, 0x01d6692e, 0xd3a0c108, 0xa1e7160e, 0xe4f2dfa6, 0x693ed285, + 0x74904698, 0x4c2b0edd, 0x4f757656, 0x5d393378, 0xa132234f, 0x3d321c5d, 0xc3f5e194, 0x4b269301, + 0xc79f022f, 0x3c997e7e, 0x5e4f9504, 0x3ffafbbd, 0x76f7ad0e, 0x296693f4, 0x3d1fce6f, 0xc61e45be, + 0xd3b5ab34, 0xf72bf9b7, 0x1b0434c0, 0x4e72b567, 0x5592a33d, 0xb5229301, 0xcfd2a87f, 0x60aeb767, + 0x1814386b, 0x30bcc33d, 0x38a0c07d, 0xfd1606f2, 0xc363519b, 0x589dd390, 0x5479f8e6, 0x1cb8d647, + 0x97fd61a9, 0xea7759f4, 0x2d57539d, 0x569a58cf, 0xe84e63ad, 0x462e1b78, 0x6580f87e, 0xf3817914, + 0x91da55f4, 0x40a230f3, 0xd1988f35, 0xb6e318d2, 0x3ffa50bc, 0x3d40f021, 0xc3c0bdae, 0x4958c24c, + 0x518f36b2, 0x84b1d370, 0x0fedce83, 0x878ddada, 0xf2a279c7, 0x94e01be8, 0x90716f4b, 0x954b8aa3); + + sBox[7] = new Array( + 0xe216300d, 0xbbddfffc, 0xa7ebdabd, 0x35648095, 0x7789f8b7, 0xe6c1121b, 0x0e241600, 0x052ce8b5, + 0x11a9cfb0, 0xe5952f11, 0xece7990a, 0x9386d174, 0x2a42931c, 0x76e38111, 0xb12def3a, 0x37ddddfc, + 0xde9adeb1, 0x0a0cc32c, 0xbe197029, 0x84a00940, 0xbb243a0f, 0xb4d137cf, 0xb44e79f0, 0x049eedfd, + 0x0b15a15d, 0x480d3168, 0x8bbbde5a, 0x669ded42, 0xc7ece831, 0x3f8f95e7, 0x72df191b, 0x7580330d, + 0x94074251, 0x5c7dcdfa, 0xabbe6d63, 0xaa402164, 0xb301d40a, 0x02e7d1ca, 0x53571dae, 0x7a3182a2, + 0x12a8ddec, 0xfdaa335d, 0x176f43e8, 0x71fb46d4, 0x38129022, 0xce949ad4, 0xb84769ad, 0x965bd862, + 0x82f3d055, 0x66fb9767, 0x15b80b4e, 0x1d5b47a0, 0x4cfde06f, 0xc28ec4b8, 0x57e8726e, 0x647a78fc, + 0x99865d44, 0x608bd593, 0x6c200e03, 0x39dc5ff6, 0x5d0b00a3, 0xae63aff2, 0x7e8bd632, 0x70108c0c, + 0xbbd35049, 0x2998df04, 0x980cf42a, 0x9b6df491, 0x9e7edd53, 0x06918548, 0x58cb7e07, 0x3b74ef2e, + 0x522fffb1, 0xd24708cc, 0x1c7e27cd, 0xa4eb215b, 0x3cf1d2e2, 0x19b47a38, 0x424f7618, 0x35856039, + 0x9d17dee7, 0x27eb35e6, 0xc9aff67b, 0x36baf5b8, 0x09c467cd, 0xc18910b1, 0xe11dbf7b, 0x06cd1af8, + 0x7170c608, 0x2d5e3354, 0xd4de495a, 0x64c6d006, 0xbcc0c62c, 0x3dd00db3, 0x708f8f34, 0x77d51b42, + 0x264f620f, 0x24b8d2bf, 0x15c1b79e, 0x46a52564, 0xf8d7e54e, 0x3e378160, 0x7895cda5, 0x859c15a5, + 0xe6459788, 0xc37bc75f, 0xdb07ba0c, 0x0676a3ab, 0x7f229b1e, 0x31842e7b, 0x24259fd7, 0xf8bef472, + 0x835ffcb8, 0x6df4c1f2, 0x96f5b195, 0xfd0af0fc, 0xb0fe134c, 0xe2506d3d, 0x4f9b12ea, 0xf215f225, + 0xa223736f, 0x9fb4c428, 0x25d04979, 0x34c713f8, 0xc4618187, 0xea7a6e98, 0x7cd16efc, 0x1436876c, + 0xf1544107, 0xbedeee14, 0x56e9af27, 0xa04aa441, 0x3cf7c899, 0x92ecbae6, 0xdd67016d, 0x151682eb, + 0xa842eedf, 0xfdba60b4, 0xf1907b75, 0x20e3030f, 0x24d8c29e, 0xe139673b, 0xefa63fb8, 0x71873054, + 0xb6f2cf3b, 0x9f326442, 0xcb15a4cc, 0xb01a4504, 0xf1e47d8d, 0x844a1be5, 0xbae7dfdc, 0x42cbda70, + 0xcd7dae0a, 0x57e85b7a, 0xd53f5af6, 0x20cf4d8c, 0xcea4d428, 0x79d130a4, 0x3486ebfb, 0x33d3cddc, + 0x77853b53, 0x37effcb5, 0xc5068778, 0xe580b3e6, 0x4e68b8f4, 0xc5c8b37e, 0x0d809ea2, 0x398feb7c, + 0x132a4f94, 0x43b7950e, 0x2fee7d1c, 0x223613bd, 0xdd06caa2, 0x37df932b, 0xc4248289, 0xacf3ebc3, + 0x5715f6b7, 0xef3478dd, 0xf267616f, 0xc148cbe4, 0x9052815e, 0x5e410fab, 0xb48a2465, 0x2eda7fa4, + 0xe87b40e4, 0xe98ea084, 0x5889e9e1, 0xefd390fc, 0xdd07d35b, 0xdb485694, 0x38d7e5b2, 0x57720101, + 0x730edebc, 0x5b643113, 0x94917e4f, 0x503c2fba, 0x646f1282, 0x7523d24a, 0xe0779695, 0xf9c17a8f, + 0x7a5b2121, 0xd187b896, 0x29263a4d, 0xba510cdf, 0x81f47c9f, 0xad1163ed, 0xea7b5965, 0x1a00726e, + 0x11403092, 0x00da6d77, 0x4a0cdd61, 0xad1f4603, 0x605bdfb0, 0x9eedc364, 0x22ebe6a8, 0xcee7d28a, + 0xa0e736a0, 0x5564a6b9, 0x10853209, 0xc7eb8f37, 0x2de705ca, 0x8951570f, 0xdf09822b, 0xbd691a6c, + 0xaa12e4f2, 0x87451c0f, 0xe0f6a27a, 0x3ada4819, 0x4cf1764f, 0x0d771c2b, 0x67cdb156, 0x350d8384, + 0x5938fa0f, 0x42399ef3, 0x36997b07, 0x0e84093d, 0x4aa93e61, 0x8360d87b, 0x1fa98b0c, 0x1149382c, + 0xe97625a5, 0x0614d1b7, 0x0e25244b, 0x0c768347, 0x589e8d82, 0x0d2059d1, 0xa466bb1e, 0xf8da0a82, + 0x04f19130, 0xba6e4ec0, 0x99265164, 0x1ee7230d, 0x50b2ad80, 0xeaee6801, 0x8db2a283, 0xea8bf59e); + +}; + diff --git a/src/ciphers/symmetric/dessrc.js b/src/ciphers/symmetric/dessrc.js new file mode 100755 index 00000000..7a125e9c --- /dev/null +++ b/src/ciphers/symmetric/dessrc.js @@ -0,0 +1,208 @@ +//Paul Tero, July 2001 +//http://www.tero.co.uk/des/ +// +//Optimised for performance with large blocks by Michael Hayworth, November 2001 +//http://www.netdealing.com +// +// Modified by Recurity Labs GmbH + +//THIS SOFTWARE IS PROVIDED "AS IS" AND +//ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +//FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +//DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +//OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +//HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +//OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +//SUCH DAMAGE. + +//des +//this takes the key, the message, and whether to encrypt or decrypt + +// added by Recurity Labs +function desede(block,key) { + var key1 = key.substring(0,8); + var key2 = key.substring(8,16); + var key3 = key.substring(16,24); + return util.str2bin(des(des_createKeys(key3),des(des_createKeys(key2),des(des_createKeys(key1),util.bin2str(block), true, 0,null,null), false, 0,null,null), true, 0,null,null)); +} + + +function des (keys, message, encrypt, mode, iv, padding) { + //declaring this locally speeds things up a bit + var spfunction1 = new Array (0x1010400,0,0x10000,0x1010404,0x1010004,0x10404,0x4,0x10000,0x400,0x1010400,0x1010404,0x400,0x1000404,0x1010004,0x1000000,0x4,0x404,0x1000400,0x1000400,0x10400,0x10400,0x1010000,0x1010000,0x1000404,0x10004,0x1000004,0x1000004,0x10004,0,0x404,0x10404,0x1000000,0x10000,0x1010404,0x4,0x1010000,0x1010400,0x1000000,0x1000000,0x400,0x1010004,0x10000,0x10400,0x1000004,0x400,0x4,0x1000404,0x10404,0x1010404,0x10004,0x1010000,0x1000404,0x1000004,0x404,0x10404,0x1010400,0x404,0x1000400,0x1000400,0,0x10004,0x10400,0,0x1010004); + var spfunction2 = new Array (-0x7fef7fe0,-0x7fff8000,0x8000,0x108020,0x100000,0x20,-0x7fefffe0,-0x7fff7fe0,-0x7fffffe0,-0x7fef7fe0,-0x7fef8000,-0x80000000,-0x7fff8000,0x100000,0x20,-0x7fefffe0,0x108000,0x100020,-0x7fff7fe0,0,-0x80000000,0x8000,0x108020,-0x7ff00000,0x100020,-0x7fffffe0,0,0x108000,0x8020,-0x7fef8000,-0x7ff00000,0x8020,0,0x108020,-0x7fefffe0,0x100000,-0x7fff7fe0,-0x7ff00000,-0x7fef8000,0x8000,-0x7ff00000,-0x7fff8000,0x20,-0x7fef7fe0,0x108020,0x20,0x8000,-0x80000000,0x8020,-0x7fef8000,0x100000,-0x7fffffe0,0x100020,-0x7fff7fe0,-0x7fffffe0,0x100020,0x108000,0,-0x7fff8000,0x8020,-0x80000000,-0x7fefffe0,-0x7fef7fe0,0x108000); + var spfunction3 = new Array (0x208,0x8020200,0,0x8020008,0x8000200,0,0x20208,0x8000200,0x20008,0x8000008,0x8000008,0x20000,0x8020208,0x20008,0x8020000,0x208,0x8000000,0x8,0x8020200,0x200,0x20200,0x8020000,0x8020008,0x20208,0x8000208,0x20200,0x20000,0x8000208,0x8,0x8020208,0x200,0x8000000,0x8020200,0x8000000,0x20008,0x208,0x20000,0x8020200,0x8000200,0,0x200,0x20008,0x8020208,0x8000200,0x8000008,0x200,0,0x8020008,0x8000208,0x20000,0x8000000,0x8020208,0x8,0x20208,0x20200,0x8000008,0x8020000,0x8000208,0x208,0x8020000,0x20208,0x8,0x8020008,0x20200); + var spfunction4 = new Array (0x802001,0x2081,0x2081,0x80,0x802080,0x800081,0x800001,0x2001,0,0x802000,0x802000,0x802081,0x81,0,0x800080,0x800001,0x1,0x2000,0x800000,0x802001,0x80,0x800000,0x2001,0x2080,0x800081,0x1,0x2080,0x800080,0x2000,0x802080,0x802081,0x81,0x800080,0x800001,0x802000,0x802081,0x81,0,0,0x802000,0x2080,0x800080,0x800081,0x1,0x802001,0x2081,0x2081,0x80,0x802081,0x81,0x1,0x2000,0x800001,0x2001,0x802080,0x800081,0x2001,0x2080,0x800000,0x802001,0x80,0x800000,0x2000,0x802080); + var spfunction5 = new Array (0x100,0x2080100,0x2080000,0x42000100,0x80000,0x100,0x40000000,0x2080000,0x40080100,0x80000,0x2000100,0x40080100,0x42000100,0x42080000,0x80100,0x40000000,0x2000000,0x40080000,0x40080000,0,0x40000100,0x42080100,0x42080100,0x2000100,0x42080000,0x40000100,0,0x42000000,0x2080100,0x2000000,0x42000000,0x80100,0x80000,0x42000100,0x100,0x2000000,0x40000000,0x2080000,0x42000100,0x40080100,0x2000100,0x40000000,0x42080000,0x2080100,0x40080100,0x100,0x2000000,0x42080000,0x42080100,0x80100,0x42000000,0x42080100,0x2080000,0,0x40080000,0x42000000,0x80100,0x2000100,0x40000100,0x80000,0,0x40080000,0x2080100,0x40000100); + var spfunction6 = new Array (0x20000010,0x20400000,0x4000,0x20404010,0x20400000,0x10,0x20404010,0x400000,0x20004000,0x404010,0x400000,0x20000010,0x400010,0x20004000,0x20000000,0x4010,0,0x400010,0x20004010,0x4000,0x404000,0x20004010,0x10,0x20400010,0x20400010,0,0x404010,0x20404000,0x4010,0x404000,0x20404000,0x20000000,0x20004000,0x10,0x20400010,0x404000,0x20404010,0x400000,0x4010,0x20000010,0x400000,0x20004000,0x20000000,0x4010,0x20000010,0x20404010,0x404000,0x20400000,0x404010,0x20404000,0,0x20400010,0x10,0x4000,0x20400000,0x404010,0x4000,0x400010,0x20004010,0,0x20404000,0x20000000,0x400010,0x20004010); + var spfunction7 = new Array (0x200000,0x4200002,0x4000802,0,0x800,0x4000802,0x200802,0x4200800,0x4200802,0x200000,0,0x4000002,0x2,0x4000000,0x4200002,0x802,0x4000800,0x200802,0x200002,0x4000800,0x4000002,0x4200000,0x4200800,0x200002,0x4200000,0x800,0x802,0x4200802,0x200800,0x2,0x4000000,0x200800,0x4000000,0x200800,0x200000,0x4000802,0x4000802,0x4200002,0x4200002,0x2,0x200002,0x4000000,0x4000800,0x200000,0x4200800,0x802,0x200802,0x4200800,0x802,0x4000002,0x4200802,0x4200000,0x200800,0,0x2,0x4200802,0,0x200802,0x4200000,0x800,0x4000002,0x4000800,0x800,0x200002); + var spfunction8 = new Array (0x10001040,0x1000,0x40000,0x10041040,0x10000000,0x10001040,0x40,0x10000000,0x40040,0x10040000,0x10041040,0x41000,0x10041000,0x41040,0x1000,0x40,0x10040000,0x10000040,0x10001000,0x1040,0x41000,0x40040,0x10040040,0x10041000,0x1040,0,0,0x10040040,0x10000040,0x10001000,0x41040,0x40000,0x41040,0x40000,0x10041000,0x1000,0x40,0x10040040,0x1000,0x41040,0x10001000,0x40,0x10000040,0x10040000,0x10040040,0x10000000,0x40000,0x10001040,0,0x10041040,0x40040,0x10000040,0x10040000,0x10001000,0x10001040,0,0x10041040,0x41000,0x41000,0x1040,0x1040,0x40040,0x10000000,0x10041000); + + //create the 16 or 48 subkeys we will need + var m=0, i, j, temp, temp2, right1, right2, left, right, looping; + var cbcleft, cbcleft2, cbcright, cbcright2 + var endloop, loopinc; + var len = message.length; + var chunk = 0; + //set up the loops for single and triple des + var iterations = keys.length == 32 ? 3 : 9; //single or triple des + if (iterations == 3) {looping = encrypt ? new Array (0, 32, 2) : new Array (30, -2, -2);} + else {looping = encrypt ? new Array (0, 32, 2, 62, 30, -2, 64, 96, 2) : new Array (94, 62, -2, 32, 64, 2, 30, -2, -2);} + + //pad the message depending on the padding parameter + if (padding == 2) message += " "; //pad the message with spaces + else if (padding == 1) {temp = 8-(len%8); message += String.fromCharCode (temp,temp,temp,temp,temp,temp,temp,temp); if (temp==8) len+=8;} //PKCS7 padding + else if (!padding) message += "\0\0\0\0\0\0\0\0"; //pad the message out with null bytes + + //store the result here + result = ""; + tempresult = ""; + + if (mode == 1) { //CBC mode + cbcleft = (iv.charCodeAt(m++) << 24) | (iv.charCodeAt(m++) << 16) | (iv.charCodeAt(m++) << 8) | iv.charCodeAt(m++); + cbcright = (iv.charCodeAt(m++) << 24) | (iv.charCodeAt(m++) << 16) | (iv.charCodeAt(m++) << 8) | iv.charCodeAt(m++); + m=0; + } + + //loop through each 64 bit chunk of the message + while (m < len) { + left = (message.charCodeAt(m++) << 24) | (message.charCodeAt(m++) << 16) | (message.charCodeAt(m++) << 8) | message.charCodeAt(m++); + right = (message.charCodeAt(m++) << 24) | (message.charCodeAt(m++) << 16) | (message.charCodeAt(m++) << 8) | message.charCodeAt(m++); + + //for Cipher Block Chaining mode, xor the message with the previous result + if (mode == 1) {if (encrypt) {left ^= cbcleft; right ^= cbcright;} else {cbcleft2 = cbcleft; cbcright2 = cbcright; cbcleft = left; cbcright = right;}} + + //first each 64 but chunk of the message must be permuted according to IP + temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4); + temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16); + temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2); + temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8); + temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1); + + left = ((left << 1) | (left >>> 31)); + right = ((right << 1) | (right >>> 31)); + + //do this either 1 or 3 times for each chunk of the message + for (j=0; j>> 4) | (right << 28)) ^ keys[i+1]; + //the result is attained by passing these bytes through the S selection functions + temp = left; + left = right; + right = temp ^ (spfunction2[(right1 >>> 24) & 0x3f] | spfunction4[(right1 >>> 16) & 0x3f] + | spfunction6[(right1 >>> 8) & 0x3f] | spfunction8[right1 & 0x3f] + | spfunction1[(right2 >>> 24) & 0x3f] | spfunction3[(right2 >>> 16) & 0x3f] + | spfunction5[(right2 >>> 8) & 0x3f] | spfunction7[right2 & 0x3f]); + } + temp = left; left = right; right = temp; //unreverse left and right + } //for either 1 or 3 iterations + + //move then each one bit to the right + left = ((left >>> 1) | (left << 31)); + right = ((right >>> 1) | (right << 31)); + + //now perform IP-1, which is IP in the opposite direction + temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1); + temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8); + temp = ((right >>> 2) ^ left) & 0x33333333; left ^= temp; right ^= (temp << 2); + temp = ((left >>> 16) ^ right) & 0x0000ffff; right ^= temp; left ^= (temp << 16); + temp = ((left >>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4); + + //for Cipher Block Chaining mode, xor the message with the previous result + if (mode == 1) {if (encrypt) {cbcleft = left; cbcright = right;} else {left ^= cbcleft2; right ^= cbcright2;}} + tempresult += String.fromCharCode ((left>>>24), ((left>>>16) & 0xff), ((left>>>8) & 0xff), (left & 0xff), (right>>>24), ((right>>>16) & 0xff), ((right>>>8) & 0xff), (right & 0xff)); + + chunk += 8; + if (chunk == 512) {result += tempresult; tempresult = ""; chunk = 0;} + } //for every 8 characters, or 64 bits in the message + + //return the result as an array + result += tempresult; + result = result.replace(/\0*$/g, ""); + return result; +} //end of des + + + +//des_createKeys +//this takes as input a 64 bit key (even though only 56 bits are used) +//as an array of 2 integers, and returns 16 48 bit keys +function des_createKeys (key) { + //declaring this locally speeds things up a bit + pc2bytes0 = new Array (0,0x4,0x20000000,0x20000004,0x10000,0x10004,0x20010000,0x20010004,0x200,0x204,0x20000200,0x20000204,0x10200,0x10204,0x20010200,0x20010204); + pc2bytes1 = new Array (0,0x1,0x100000,0x100001,0x4000000,0x4000001,0x4100000,0x4100001,0x100,0x101,0x100100,0x100101,0x4000100,0x4000101,0x4100100,0x4100101); + pc2bytes2 = new Array (0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808,0,0x8,0x800,0x808,0x1000000,0x1000008,0x1000800,0x1000808); + pc2bytes3 = new Array (0,0x200000,0x8000000,0x8200000,0x2000,0x202000,0x8002000,0x8202000,0x20000,0x220000,0x8020000,0x8220000,0x22000,0x222000,0x8022000,0x8222000); + pc2bytes4 = new Array (0,0x40000,0x10,0x40010,0,0x40000,0x10,0x40010,0x1000,0x41000,0x1010,0x41010,0x1000,0x41000,0x1010,0x41010); + pc2bytes5 = new Array (0,0x400,0x20,0x420,0,0x400,0x20,0x420,0x2000000,0x2000400,0x2000020,0x2000420,0x2000000,0x2000400,0x2000020,0x2000420); + pc2bytes6 = new Array (0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002,0,0x10000000,0x80000,0x10080000,0x2,0x10000002,0x80002,0x10080002); + pc2bytes7 = new Array (0,0x10000,0x800,0x10800,0x20000000,0x20010000,0x20000800,0x20010800,0x20000,0x30000,0x20800,0x30800,0x20020000,0x20030000,0x20020800,0x20030800); + pc2bytes8 = new Array (0,0x40000,0,0x40000,0x2,0x40002,0x2,0x40002,0x2000000,0x2040000,0x2000000,0x2040000,0x2000002,0x2040002,0x2000002,0x2040002); + pc2bytes9 = new Array (0,0x10000000,0x8,0x10000008,0,0x10000000,0x8,0x10000008,0x400,0x10000400,0x408,0x10000408,0x400,0x10000400,0x408,0x10000408); + pc2bytes10 = new Array (0,0x20,0,0x20,0x100000,0x100020,0x100000,0x100020,0x2000,0x2020,0x2000,0x2020,0x102000,0x102020,0x102000,0x102020); + pc2bytes11 = new Array (0,0x1000000,0x200,0x1000200,0x200000,0x1200000,0x200200,0x1200200,0x4000000,0x5000000,0x4000200,0x5000200,0x4200000,0x5200000,0x4200200,0x5200200); + pc2bytes12 = new Array (0,0x1000,0x8000000,0x8001000,0x80000,0x81000,0x8080000,0x8081000,0x10,0x1010,0x8000010,0x8001010,0x80010,0x81010,0x8080010,0x8081010); + pc2bytes13 = new Array (0,0x4,0x100,0x104,0,0x4,0x100,0x104,0x1,0x5,0x101,0x105,0x1,0x5,0x101,0x105); + + //how many iterations (1 for des, 3 for triple des) + var iterations = key.length > 8 ? 3 : 1; //changed by Paul 16/6/2007 to use Triple DES for 9+ byte keys + //stores the return keys + var keys = new Array (32 * iterations); + //now define the left shifts which need to be done + var shifts = new Array (0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0); + //other variables + var lefttemp, righttemp, m=0, n=0, temp; + + for (var j=0; j>> 4) ^ right) & 0x0f0f0f0f; right ^= temp; left ^= (temp << 4); + temp = ((right >>> -16) ^ left) & 0x0000ffff; left ^= temp; right ^= (temp << -16); + temp = ((left >>> 2) ^ right) & 0x33333333; right ^= temp; left ^= (temp << 2); + temp = ((right >>> -16) ^ left) & 0x0000ffff; left ^= temp; right ^= (temp << -16); + temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1); + temp = ((right >>> 8) ^ left) & 0x00ff00ff; left ^= temp; right ^= (temp << 8); + temp = ((left >>> 1) ^ right) & 0x55555555; right ^= temp; left ^= (temp << 1); + + //the right side needs to be shifted and to get the last four bits of the left side + temp = (left << 8) | ((right >>> 20) & 0x000000f0); + //left needs to be put upside down + left = (right << 24) | ((right << 8) & 0xff0000) | ((right >>> 8) & 0xff00) | ((right >>> 24) & 0xf0); + right = temp; + + //now go through and perform these shifts on the left and right keys + for (i=0; i < shifts.length; i++) { + //shift the keys either one or two bits to the left + if (shifts[i]) {left = (left << 2) | (left >>> 26); right = (right << 2) | (right >>> 26);} + else {left = (left << 1) | (left >>> 27); right = (right << 1) | (right >>> 27);} + left &= -0xf; right &= -0xf; + + //now apply PC-2, in such a way that E is easier when encrypting or decrypting + //this conversion will look like PC-2 except only the last 6 bits of each byte are used + //rather than 48 consecutive bits and the order of lines will be according to + //how the S selection functions will be applied: S2, S4, S6, S8, S1, S3, S5, S7 + lefttemp = pc2bytes0[left >>> 28] | pc2bytes1[(left >>> 24) & 0xf] + | pc2bytes2[(left >>> 20) & 0xf] | pc2bytes3[(left >>> 16) & 0xf] + | pc2bytes4[(left >>> 12) & 0xf] | pc2bytes5[(left >>> 8) & 0xf] + | pc2bytes6[(left >>> 4) & 0xf]; + righttemp = pc2bytes7[right >>> 28] | pc2bytes8[(right >>> 24) & 0xf] + | pc2bytes9[(right >>> 20) & 0xf] | pc2bytes10[(right >>> 16) & 0xf] + | pc2bytes11[(right >>> 12) & 0xf] | pc2bytes12[(right >>> 8) & 0xf] + | pc2bytes13[(right >>> 4) & 0xf]; + temp = ((righttemp >>> 16) ^ lefttemp) & 0x0000ffff; + keys[n++] = lefttemp ^ temp; keys[n++] = righttemp ^ (temp << 16); + } + } //for each iterations + //return the keys we've created + return keys; +} //end of des_createKeys + + diff --git a/src/ciphers/symmetric/twofish.js b/src/ciphers/symmetric/twofish.js new file mode 100755 index 00000000..ba0d6646 --- /dev/null +++ b/src/ciphers/symmetric/twofish.js @@ -0,0 +1,302 @@ +/* Modified by Recurity Labs GmbH + * + * Cipher.js + * A block-cipher algorithm implementation on JavaScript + * See Cipher.readme.txt for further information. + * + * Copyright(c) 2009 Atsushi Oka [ http://oka.nu/ ] + * This script file is distributed under the LGPL + * + * ACKNOWLEDGMENT + * + * The main subroutines are written by Michiel van Everdingen. + * + * Michiel van Everdingen + * http://home.versatel.nl/MAvanEverdingen/index.html + * + * All rights for these routines are reserved to Michiel van Everdingen. + * + */ + +// added by Recurity Labs +function TFencrypt(block, key) { + var block_copy = [].concat(block); + var tf = createTwofish(); + tf.open(util.str2bin(key),0); + var result = tf.encrypt(block_copy, 0); + tf.close(); + return result; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//Math +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +var MAXINT = 0xFFFFFFFF; + +function rotb(b,n){ return ( b<>>( 8-n) ) & 0xFF; } +function rotw(w,n){ return ( w<>>(32-n) ) & MAXINT; } +function getW(a,i){ return a[i]|a[i+1]<<8|a[i+2]<<16|a[i+3]<<24; } +function setW(a,i,w){ a.splice(i,4,w&0xFF,(w>>>8)&0xFF,(w>>>16)&0xFF,(w>>>24)&0xFF); } +function setWInv(a,i,w){ a.splice(i,4,(w>>>24)&0xFF,(w>>>16)&0xFF,(w>>>8)&0xFF,w&0xFF); } +function getB(x,n){ return (x>>>(n*8))&0xFF; } + +function getNrBits(i){ var n=0; while (i>0){ n++; i>>>=1; } return n; } +function getMask(n){ return (1<> 2) ^ [ 0, 90, 180, 238 ][x & 3]; + } + function ffmEf(x) { + return x ^ (x >> 1) ^ (x >> 2) ^ [ 0, 238, 180, 90 ][x & 3]; + } + + function mdsRem(p, q) { + var i, t, u; + for (i = 0; i < 8; i++) { + t = q >>> 24; + q = ((q << 8) & MAXINT) | p >>> 24; + p = (p << 8) & MAXINT; + u = t << 1; + if (t & 128) { + u ^= 333; + } + q ^= t ^ (u << 16); + u ^= t >>> 1; + if (t & 1) { + u ^= 166; + } + q ^= u << 24 | u << 8; + } + return q; + } + + function qp(n, x) { + var a, b, c, d; + a = x >> 4; + b = x & 15; + c = q0[n][a ^ b]; + d = q1[n][ror4[b] ^ ashx[a]]; + return q3[n][ror4[d] ^ ashx[c]] << 4 | q2[n][c ^ d]; + } + + function hFun(x, key) { + var a = getB(x, 0), b = getB(x, 1), c = getB(x, 2), d = getB(x, 3); + switch (kLen) { + case 4: + a = q[1][a] ^ getB(key[3], 0); + b = q[0][b] ^ getB(key[3], 1); + c = q[0][c] ^ getB(key[3], 2); + d = q[1][d] ^ getB(key[3], 3); + case 3: + a = q[1][a] ^ getB(key[2], 0); + b = q[1][b] ^ getB(key[2], 1); + c = q[0][c] ^ getB(key[2], 2); + d = q[0][d] ^ getB(key[2], 3); + case 2: + a = q[0][q[0][a] ^ getB(key[1], 0)] ^ getB(key[0], 0); + b = q[0][q[1][b] ^ getB(key[1], 1)] ^ getB(key[0], 1); + c = q[1][q[0][c] ^ getB(key[1], 2)] ^ getB(key[0], 2); + d = q[1][q[1][d] ^ getB(key[1], 3)] ^ getB(key[0], 3); + } + return m[0][a] ^ m[1][b] ^ m[2][c] ^ m[3][d]; + } + + keyBytes = keyBytes.slice(0, 32); + i = keyBytes.length; + while (i != 16 && i != 24 && i != 32) + keyBytes[i++] = 0; + + for (i = 0; i < keyBytes.length; i += 4) { + inKey[i >> 2] = getW(keyBytes, i); + } + for (i = 0; i < 256; i++) { + q[0][i] = qp(0, i); + q[1][i] = qp(1, i); + } + for (i = 0; i < 256; i++) { + f01 = q[1][i]; + f5b = ffm5b(f01); + fef = ffmEf(f01); + m[0][i] = f01 + (f5b << 8) + (fef << 16) + (fef << 24); + m[2][i] = f5b + (fef << 8) + (f01 << 16) + (fef << 24); + f01 = q[0][i]; + f5b = ffm5b(f01); + fef = ffmEf(f01); + m[1][i] = fef + (fef << 8) + (f5b << 16) + (f01 << 24); + m[3][i] = f5b + (f01 << 8) + (fef << 16) + (f5b << 24); + } + + kLen = inKey.length / 2; + for (i = 0; i < kLen; i++) { + a = inKey[i + i]; + meKey[i] = a; + b = inKey[i + i + 1]; + moKey[i] = b; + sKey[kLen - i - 1] = mdsRem(a, b); + } + for (i = 0; i < 40; i += 2) { + a = 0x1010101 * i; + b = a + 0x1010101; + a = hFun(a, meKey); + b = rotw(hFun(b, moKey), 8); + tfsKey[i] = (a + b) & MAXINT; + tfsKey[i + 1] = rotw(a + 2 * b, 9); + } + for (i = 0; i < 256; i++) { + a = b = c = d = i; + switch (kLen) { + case 4: + a = q[1][a] ^ getB(sKey[3], 0); + b = q[0][b] ^ getB(sKey[3], 1); + c = q[0][c] ^ getB(sKey[3], 2); + d = q[1][d] ^ getB(sKey[3], 3); + case 3: + a = q[1][a] ^ getB(sKey[2], 0); + b = q[1][b] ^ getB(sKey[2], 1); + c = q[0][c] ^ getB(sKey[2], 2); + d = q[0][d] ^ getB(sKey[2], 3); + case 2: + tfsM[0][i] = m[0][q[0][q[0][a] ^ getB(sKey[1], 0)] + ^ getB(sKey[0], 0)]; + tfsM[1][i] = m[1][q[0][q[1][b] ^ getB(sKey[1], 1)] + ^ getB(sKey[0], 1)]; + tfsM[2][i] = m[2][q[1][q[0][c] ^ getB(sKey[1], 2)] + ^ getB(sKey[0], 2)]; + tfsM[3][i] = m[3][q[1][q[1][d] ^ getB(sKey[1], 3)] + ^ getB(sKey[0], 3)]; + } + } + } + + function tfsG0(x) { + return tfsM[0][getB(x, 0)] ^ tfsM[1][getB(x, 1)] ^ tfsM[2][getB(x, 2)] + ^ tfsM[3][getB(x, 3)]; + } + function tfsG1(x) { + return tfsM[0][getB(x, 3)] ^ tfsM[1][getB(x, 0)] ^ tfsM[2][getB(x, 1)] + ^ tfsM[3][getB(x, 2)]; + } + + function tfsFrnd(r, blk) { + var a = tfsG0(blk[0]); + var b = tfsG1(blk[1]); + blk[2] = rotw(blk[2] ^ (a + b + tfsKey[4 * r + 8]) & MAXINT, 31); + blk[3] = rotw(blk[3], 1) ^ (a + 2 * b + tfsKey[4 * r + 9]) & MAXINT; + a = tfsG0(blk[2]); + b = tfsG1(blk[3]); + blk[0] = rotw(blk[0] ^ (a + b + tfsKey[4 * r + 10]) & MAXINT, 31); + blk[1] = rotw(blk[1], 1) ^ (a + 2 * b + tfsKey[4 * r + 11]) & MAXINT; + } + + function tfsIrnd(i, blk) { + var a = tfsG0(blk[0]); + var b = tfsG1(blk[1]); + blk[2] = rotw(blk[2], 1) ^ (a + b + tfsKey[4 * i + 10]) & MAXINT; + blk[3] = rotw(blk[3] ^ (a + 2 * b + tfsKey[4 * i + 11]) & MAXINT, 31); + a = tfsG0(blk[2]); + b = tfsG1(blk[3]); + blk[0] = rotw(blk[0], 1) ^ (a + b + tfsKey[4 * i + 8]) & MAXINT; + blk[1] = rotw(blk[1] ^ (a + 2 * b + tfsKey[4 * i + 9]) & MAXINT, 31); + } + + function tfsClose() { + tfsKey = []; + tfsM = [ [], [], [], [] ]; + } + + function tfsEncrypt(data, offset) { + dataBytes = data; + dataOffset = offset; + var blk = [ getW(dataBytes, dataOffset) ^ tfsKey[0], + getW(dataBytes, dataOffset + 4) ^ tfsKey[1], + getW(dataBytes, dataOffset + 8) ^ tfsKey[2], + getW(dataBytes, dataOffset + 12) ^ tfsKey[3] ]; + for ( var j = 0; j < 8; j++) { + tfsFrnd(j, blk); + } + setW(dataBytes, dataOffset, blk[2] ^ tfsKey[4]); + setW(dataBytes, dataOffset + 4, blk[3] ^ tfsKey[5]); + setW(dataBytes, dataOffset + 8, blk[0] ^ tfsKey[6]); + setW(dataBytes, dataOffset + 12, blk[1] ^ tfsKey[7]); + dataOffset += 16; + return dataBytes; + } + + function tfsDecrypt(data, offset) { + dataBytes = data; + dataOffset = offset; + var blk = [ getW(dataBytes, dataOffset) ^ tfsKey[4], + getW(dataBytes, dataOffset + 4) ^ tfsKey[5], + getW(dataBytes, dataOffset + 8) ^ tfsKey[6], + getW(dataBytes, dataOffset + 12) ^ tfsKey[7] ]; + for ( var j = 7; j >= 0; j--) { + tfsIrnd(j, blk); + } + setW(dataBytes, dataOffset, blk[2] ^ tfsKey[0]); + setW(dataBytes, dataOffset + 4, blk[3] ^ tfsKey[1]); + setW(dataBytes, dataOffset + 8, blk[0] ^ tfsKey[2]); + setW(dataBytes, dataOffset + 12, blk[1] ^ tfsKey[3]); + dataOffset += 16; + } + + // added by Recurity Labs + function tfsFinal() { + return dataBytes; + } + + return { + name : "twofish", + blocksize : 128 / 8, + open : tfsInit, + close : tfsClose, + encrypt : tfsEncrypt, + decrypt : tfsDecrypt, + // added by Recurity Labs + finalize: tfsFinal + }; +} + diff --git a/src/config/openpgp.config.js b/src/config/openpgp.config.js new file mode 100755 index 00000000..5786b1f6 --- /dev/null +++ b/src/config/openpgp.config.js @@ -0,0 +1,78 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * Implementation of the GPG4Browsers config object + * + * This object contains configuration values and implements + * storing and retrieving configuration them from HTML5 local storage. + * + * This object can be accessed after calling openpgp.init() + * using openpgp.config + * Stored config parameters can be accessed using + * openpgp.config.config + */ +function openpgp_config() { + this.config = null; + + /** + * the default config object which is used if no + * configuration was in place + */ + this.default_config = { + prefer_hash_algorithm: 2, + encryption_cipher: 9, + compression: 1, + show_version: true, + integrity_protect: true, + composition_behavior: 0, + keyserver: "keyserver.linux.it" // "pgp.mit.edu:11371" + }; + + this.versionstring ="GPG4Browsers 0.1"; + /** + * reads the config out of the HTML5 local storage + * and initializes the object config. + * if config is null the default config will be used + * @return [void] + */ + function read() { + var cf = JSON.parse(window.localStorage.getItem("config")); + if (cf == null) { + this.config = this.default_config; + this.write(); + } + else + this.config = cf; + } + + /** + * if enabled, debug messages will be printed + */ + this.debug = false; + + /** + * writes the config to HTML5 local storage + * @return [void] + */ + function write() { + window.localStorage.setItem("config",JSON.stringify(this.config)); + } + + this.read = read; + this.write = write; +} \ No newline at end of file diff --git a/src/encoding/base64.js b/src/encoding/base64.js new file mode 100755 index 00000000..288eaf36 --- /dev/null +++ b/src/encoding/base64.js @@ -0,0 +1,76 @@ +/* OpenPGP radix-64/base64 string encoding/decoding + * Copyright 2005 Herbert Hanewinkel, www.haneWIN.de + * version 1.0, check www.haneWIN.de for the latest version + * + * This software is provided as-is, without express or implied warranty. + * Permission to use, copy, modify, distribute or sell this software, with or + * without fee, for any purpose and by any individual or organization, is hereby + * granted, provided that the above copyright notice and this paragraph appear + * in all copies. Distribution as a part of an application or binary must + * include the above copyright notice in the documentation and/or other materials + * provided with the application or distribution. + */ + +var b64s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; + +function s2r(t) { + var a, c, n; + var r = '', l = 0, s = 0; + var tl = t.length; + + for (n = 0; n < tl; n++) { + c = t.charCodeAt(n); + if (s == 0) { + r += b64s.charAt((c >> 2) & 63); + a = (c & 3) << 4; + } else if (s == 1) { + r += b64s.charAt((a | (c >> 4) & 15)); + a = (c & 15) << 2; + } else if (s == 2) { + r += b64s.charAt(a | ((c >> 6) & 3)); + l += 1; + if ((l % 60) == 0) + r += "\n"; + r += b64s.charAt(c & 63); + } + l += 1; + if ((l % 60) == 0) + r += "\n"; + + s += 1; + if (s == 3) + s = 0; + } + if (s > 0) { + r += b64s.charAt(a); + l += 1; + if ((l % 60) == 0) + r += "\n"; + r += '='; + l += 1; + } + if (s == 1) { + if ((l % 60) == 0) + r += "\n"; + r += '='; + } + + return r; +} + +function r2s(t) { + var c, n; + var r = '', s = 0, a = 0; + var tl = t.length; + + for (n = 0; n < tl; n++) { + c = b64s.indexOf(t.charAt(n)); + if (c >= 0) { + if (s) + r += String.fromCharCode(a | (c >> (6 - s)) & 255); + s = (s + 2) & 7; + a = (c << s) & 255; + } + } + return r; +} diff --git a/src/encoding/openpgp.encoding.asciiarmor.js b/src/encoding/openpgp.encoding.asciiarmor.js new file mode 100755 index 00000000..653bcac7 --- /dev/null +++ b/src/encoding/openpgp.encoding.asciiarmor.js @@ -0,0 +1,230 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * DeArmor an OpenPGP armored message; verify the checksum and return the encoded bytes + * @text [String] OpenPGP armored message + * @return either the bytes of the decoded message or an object with attribute "text" containing the message text + * and an attribute "openpgp" containing the bytes. + */ +function openpgp_encoding_deArmor(text) { + var type = getPGPMessageType(text); + if (type != 2) { + var splittedtext = text.split('-----'); + data = { openpgp: openpgp_encoding_base64_decode(splittedtext[2].split('\n\n')[1].split("\n=")[0].replace(/\n- /g,"\n")), + type: type}; + if (verifyCheckSum(data.openpgp, splittedtext[2].split('\n\n')[1].split("\n=")[1].split('\n')[0])) + return data; + else + util.print_error("Ascii armor integrity check on message failed: '"+splittedtext[2].split('\n\n')[1].split("\n=")[1].split('\n')[0]+"' should be '"+getCheckSum(data))+"'"; + } else { + var splittedtext = text.split('-----'); + var result = { text: splittedtext[2].replace(/\n- /g,"\n").split("\n\n")[1], + openpgp: openpgp_encoding_base64_decode(splittedtext[4].split("\n\n")[1].split("\n=")[0]), + type: type}; + if (verifyCheckSum(result.openpgp, splittedtext[4].split("\n\n")[1].split("\n=")[1])) + return result; + else + util.print_error("Ascii armor integrity check on message failed"); + } +} + +/** + * Finds out which Ascii Armoring type is used. This is an internal function + * @param text [String] ascii armored text + * @return 0 = MESSAGE PART n of m + * 1 = MESSAGE PART n + * 2 = SIGNED MESSAGE + * 3 = PGP MESSAGE + * 4 = PUBLIC KEY BLOCK + * 5 = PRIVATE KEY BLOCK + * null = unknown + */ +function getPGPMessageType(text) { + var splittedtext = text.split('-----'); + // BEGIN PGP MESSAGE, PART X/Y + // Used for multi-part messages, where the armor is split amongst Y + // parts, and this is the Xth part out of Y. + if (splittedtext[1].match(/BEGIN PGP MESSAGE, PART \d+\/\d+/)) { + return 0; + } else + // BEGIN PGP MESSAGE, PART X + // Used for multi-part messages, where this is the Xth part of an + // unspecified number of parts. Requires the MESSAGE-ID Armor + // Header to be used. + if (splittedtext[1].match(/BEGIN PGP MESSAGE, PART \d+/)) { + return 1; + + } else + // BEGIN PGP SIGNATURE + // Used for detached signatures, OpenPGP/MIME signatures, and + // cleartext signatures. Note that PGP 2.x uses BEGIN PGP MESSAGE + // for detached signatures. + if (splittedtext[1].match(/BEGIN PGP SIGNED MESSAGE/)) { + return 2; + + } else + // BEGIN PGP MESSAGE + // Used for signed, encrypted, or compressed files. + if (splittedtext[1].match(/BEGIN PGP MESSAGE/)) { + return 3; + + } else + // BEGIN PGP PUBLIC KEY BLOCK + // Used for armoring public keys. + if (splittedtext[1].match(/BEGIN PGP PUBLIC KEY BLOCK/)) { + return 4; + + } else + // BEGIN PGP PRIVATE KEY BLOCK + // Used for armoring private keys. + if (splittedtext[1].match(/BEGIN PGP PRIVATE KEY BLOCK/)) { + return 5; + } +} + +/** + * Armor an OpenPGP binary packet block + * @param messagetype type of the message + * @param data + * @param partindex + * @param parttotal + * @return + */ +function openpgp_encoding_armor(messagetype, data, partindex, parttotal) { + var result = ""; + switch(messagetype) { + case 0: + result += "-----BEGIN PGP MESSAGE, PART "+partindex+"/"+parttotal+"-----\r\n"; + if (openpgp.config.config.show_version) { + result += "Version: "+openpgp.config.versionstring+'\r\n\r\n'; + } else { + result += '\r\n'; + } + result += openpgp_encoding_base64_encode(data); + result += "\r\n="+getCheckSum(data)+"\r\n"; + result += "-----END PGP MESSAGE, PART "+partindex+"/"+parttotal+"-----\r\n"; + break; + case 1: + result += "-----BEGIN PGP MESSAGE, PART "+partindex+"-----\r\n"; + if (openpgp.config.config.show_version) { + result += "Version: "+openpgp.config.versionstring+'\r\n\r\n'; + } else { + result += '\r\n'; + } + + result += openpgp_encoding_base64_encode(data); + result += "\r\n="+getCheckSum(data)+"\r\n"; + result += "-----END PGP MESSAGE, PART "+partindex+"-----\r\n"; + break; + case 2: + result += "\r\n-----BEGIN PGP SIGNED MESSAGE-----\r\nHash: "+data.hash+"\r\n\r\n"; + result += data.text.replace(/\n-/g,"\n- -"); + result += "\r\n-----BEGIN PGP SIGNATURE-----\r\n"; + if (openpgp.config.config.show_version) { + result += "Version: "+openpgp.config.versionstring+'\r\n\r\n'; + } else { + result += '\r\n'; + } + result += openpgp_encoding_base64_encode(data.openpgp); + result += "\r\n="+getCheckSum(data.openpgp)+"\r\n"; + result += "-----END PGP SIGNATURE-----\r\n"; + break; + case 3: + result += "-----BEGIN PGP MESSAGE-----\r\n"; + if (openpgp.config.config.show_version) { + result +="Version: "+ openpgp.config.versionstring+'\r\n\r\n'; + } else { + result += '\r\n'; + } + + result += openpgp_encoding_base64_encode(data); + result += "\r\n="+getCheckSum(data)+"\r\n"; + result += "-----END PGP MESSAGE-----\r\n"; + break; + case 4: + result += "-----BEGIN PGP PUBLIC KEY BLOCK-----\r\n"; + if (openpgp.config.config.show_version) { + result += "Version: "+openpgp.config.versionstring+'\r\n\r\n'; + } else { + result += '\r\n'; + } + + result += openpgp_encoding_base64_encode(data); + result += "\r\n="+getCheckSum(data)+"\r\n"; + result += "-----END PGP PUBLIC KEY BLOCK-----\r\n\r\n"; + break; + case 5: + result += "-----BEGIN PGP PRIVATE KEY BLOCK-----\r\n"; + if (openpgp.config.config.show_version) { + result += "Version: "+openpgp.config.versionstring+'\r\n\r\n'; + } else { + result += '\r\n'; + } + result += openpgp_encoding_base64_encode(data); + result += "\r\n="+getCheckSum(data)+"\r\n"; + result += "-----END PGP PRIVATE KEY BLOCK-----\r\n"; + break; + } + + return result; +} + +/** + * Calculates a checksum over the given data and returns it base64 encoded + * @param data [String] data to create a CRC-24 checksum for + * @return [String] base64 encoded checksum + */ +function getCheckSum(data) { + var c = createcrc24(data); + var str = "" + String.fromCharCode(c >> 16)+ + String.fromCharCode((c >> 8) & 0xFF)+ + String.fromCharCode(c & 0xFF); + return openpgp_encoding_base64_encode(str); +} + +/** + * Calculates the checksum over the given data and compares it with the given base64 encoded checksum + * @param data [String] data to create a CRC-24 checksum for + * @param checksum [String] base64 encoded checksum + * @return true if the given checksum is correct; otherwise false + */ +function verifyCheckSum(data, checksum) { + var c = getCheckSum(data); + var d = checksum; + return c[0] == d[0] && c[1] == d[1] && c[2] == d[2]; +} +/** + * Internal function to calculate a CRC-24 checksum over a given string (data) + * @param data [String] data to create a CRC-24 checksum for + * @return [Integer] the CRC-24 checksum as number + */ +function createcrc24 (data) { + var crc = 0xB704CE; + var i; + var mypos = 0; + var len = data.length; + while (len--) { + crc ^= (data[mypos++].charCodeAt()) << 16; + for (i = 0; i < 8; i++) { + crc <<= 1; + if (crc & 0x1000000) + crc ^= 0x1864CFB; + } + } + return crc & 0xFFFFFF; +} diff --git a/src/encoding/openpgp.encoding.js b/src/encoding/openpgp.encoding.js new file mode 100755 index 00000000..89323cf5 --- /dev/null +++ b/src/encoding/openpgp.encoding.js @@ -0,0 +1,125 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * Wrapper function for the base64 codec. + * This function encodes a String (message) in base64 (radix-64) + * @param message [String] the message to encode + * @return [String] the base64 encoded data + */ +function openpgp_encoding_base64_encode(message) { + return s2r(message); +} + + +/** + * Wrapper function for the base64 codec. + * This function decodes a String(message) in base64 (radix-64) + * @param message [String] base64 encoded data + * @return [String] raw data after decoding + */ +function openpgp_encoding_base64_decode(message) { + return r2s(message); +} + +/** + * Wrapper function for jquery library. + * This function escapes HTML characters within a string. This is used to prevent XSS. + * @param message [String] message to escape + * @return [String] html encoded string + */ +function openpgp_encoding_html_encode(message) { + if (message == null) + return ""; + return $('
').text(message).html(); +} + +/** + * create a EME-PKCS1-v1_5 padding (See RFC4880 13.1.1) + * @param message [String] message to be padded + * @param length [Integer] length to the resulting message + * @return [String] EME-PKCS1 padded message + */ +function openpgp_encoding_eme_pkcs1_encode(message, length) { + if (message.length > length-11) + return -1; + var result = ""; + result += String.fromCharCode(0); + result += String.fromCharCode(2); + for (var i = 0; i < length - message.length - 3; i++) { + result += String.fromCharCode(openpgp_crypto_getPseudoRandom(1,255)); + } + result += String.fromCharCode(0); + result += message; + return result; +} + +/** + * ASN1 object identifiers for hashes (See RFC4880 5.2.2) + */ +hash_headers = new Array(); +hash_headers[1] = [0x30,0x20,0x30,0x0c,0x06,0x08,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x02,0x05,0x05,0x00,0x04,0x10]; +hash_headers[3] = [0x30,0x21,0x30,0x09,0x06,0x05,0x2B,0x24,0x03,0x02,0x01,0x05,0x00,0x04,0x14]; +hash_headers[2] = [0x30,0x21,0x30,0x09,0x06,0x05,0x2b,0x0e,0x03,0x02,0x1a,0x05,0x00,0x04,0x14]; +hash_headers[8] = [0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x01,0x05,0x00,0x04,0x20]; +hash_headers[9] = [0x30,0x41,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x02,0x05,0x00,0x04,0x30]; +hash_headers[10] = [0x30,0x51,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x03,0x05,0x00,0x04,0x40]; +hash_headers[11] = [0x30,0x31,0x30,0x0d,0x06,0x09,0x60,0x86,0x48,0x01,0x65,0x03,0x04,0x02,0x04,0x05,0x00,0x04,0x1C]; + +/** + * create a EMSA-PKCS1-v1_5 padding (See RFC4880 13.1.3) + * @param algo [Integer] hash algorithm type used + * @param data [String] data to be hashed + * @param keylength [Integer] key size of the public mpi in bytes + * @return the [String] hashcode with pkcs1padding as string + */ +function openpgp_encoding_emsa_pkcs1_encode(algo, data, keylength) { + var data2 = ""; + data2 += String.fromCharCode(0x00); + data2 += String.fromCharCode(0x01); + for (var i = 0; i < (keylength - hash_headers[algo].length - 3 - openpgp_crypto_getHashByteLength(algo)); i++) + data2 += String.fromCharCode(0xff); + data2 += String.fromCharCode(0x00); + + for (var i = 0; i < hash_headers[algo].length; i++) + data2 += String.fromCharCode(hash_headers[algo][i]); + + data2 += openpgp_crypto_hashData(algo, data); + return new BigInteger(util.hexstrdump(data2),16); +} + +/** + * extract the hash out of an EMSA-PKCS1-v1.5 padding (See RFC4880 13.1.3) + * @param data [String] hash in pkcs1 encoding + * @return the hash as string + */ +function openpgp_encoding_emsa_pkcs1_decode(algo, data) { + var i = 0; + if (data.charCodeAt(0) == 0) i++; + else if (data.charCodeAt(0) != 1) return -1; + else i++; + + while (data.charCodeAt(i) == 0xFF) i++; + if (data.charCodeAt(i++) != 0) return -1; + var j = 0; + for (j = 0; j < hash_headers[algo].length && j+i < data.length; j++) { + if (data.charCodeAt(j+i) != hash_headers[algo][j]) return -1; + } + i+= j; + if (data.substring(i).length < openpgp_crypto_getHashByteLength(algo)) return -1; + return data.substring(i); +} \ No newline at end of file diff --git a/src/openpgp.js b/src/openpgp.js new file mode 100755 index 00000000..080ca1f7 --- /dev/null +++ b/src/openpgp.js @@ -0,0 +1,357 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * GPG4Browsers Core interface. A single instance is hold + * from the beginning. To use this library call "openpgp.init()" + */ +function _openpgp () { + this.tostring = ""; + + /** + * initializes the library: + * - reading the keyring from local storage + * - reading the config from local storage + * @return [void] + */ + function init() { + this.config = new openpgp_config(); + this.config.read(); + this.keyring = new openpgp_keyring(); + this.keyring.init(); + } + + /** + * reads several publicKey objects from a ascii armored + * representation an returns openpgp_msg_publickey packets + * @param armoredText [String] OpenPGP armored text containing + * the public key(s) + * @return [Array[openpgp_msg_publickey]] on error the function + * returns null + */ + function read_publicKey(armoredText) { + var mypos = 0; + var publicKeys = new Array(); + var publicKeyCount = 0; + var input = openpgp_encoding_deArmor(armoredText.replace(/\r/g,'')).openpgp; + var l = input.length; + while (mypos != input.length) { + var first_packet = openpgp_packet.read_packet(input, mypos, l); + // public key parser + if (input[mypos].charCodeAt() == 0x99 || first_packet.tagType == 6) { + publicKeys[publicKeyCount] = new openpgp_msg_publickey(); + publicKeys[publicKeyCount].header = input.substring(mypos,mypos+3); + if (input[mypos].charCodeAt() == 0x99) { + // parse the length and read a tag6 packet + mypos++; + var l = (input[mypos++].charCodeAt() << 8) + | input[mypos++].charCodeAt(); + publicKeys[publicKeyCount].publicKeyPacket = new openpgp_packet_keymaterial(); + publicKeys[publicKeyCount].publicKeyPacket.header = publicKeys[publicKeyCount].header; + publicKeys[publicKeyCount].publicKeyPacket.read_tag6(input, mypos, l); + mypos += publicKeys[publicKeyCount].publicKeyPacket.packetLength; + mypos += publicKeys[publicKeyCount].read_nodes(publicKeys[publicKeyCount].publicKeyPacket, input, mypos, (input.length - mypos)); + } else { + publicKeys[publicKeys.length] = new openpgp_msg_publickey(); + publicKeys[publicKeyCount].publicKeyPacket = first_packet; + mypos += first_packet.headerLength+first_packet.packetLength; + mypos += publicKeys[publicKeyCount].read_nodes(first_packet, input, mypos, input.length -mypos); + } + } else { + util.print_error("no public key found!"); + return null; + } + publicKeys[publicKeyCount].data = input.substring(0,mypos); + publicKeyCount++; + } + return publicKeys; + } + + /** + * reads several privateKey objects from a ascii armored + * representation an returns openpgp_msg_privatekey objects + * @param armoredText [String] OpenPGP armored text containing + * the private key(s) + * @return [Array[openpgp_msg_privatekey]] on error the function + * returns null + */ + function read_privateKey(armoredText) { + var privateKeys = new Array(); + var privateKeyCount = 0; + var mypos = 0; + var input = openpgp_encoding_deArmor(armoredText.replace(/\r/g,'')).openpgp; + var l = input.length; + while (mypos != input.length) { + var first_packet = openpgp_packet.read_packet(input, mypos, l); + if (first_packet.tagType == 5) { + privateKeys[privateKeys.length] = new openpgp_msg_privatekey(); + mypos += first_packet.headerLength+first_packet.packetLength; + mypos += privateKeys[privateKeyCount].read_nodes(first_packet, input, mypos, l); + // other blocks + } else { + util.print_error('no block packet found!'); + return null; + } + privateKeys[privateKeyCount].data = input.substring(0,mypos); + privateKeyCount++; + } + return privateKeys; + } + + /** + * reads message packets out of an OpenPGP armored text and + * returns an array of message objects + * @param armoredText [String] text to be parsed + * @return [Array[openpgp_msg_message]] on error the function + * returns null + */ + function read_message(armoredText) { + var dearmored = openpgp_encoding_deArmor(armoredText.replace(/\r/g,'')); + var input = dearmored.openpgp; + var messages = new Array(); + var messageCount = 0; + var mypos = 0; + var l = input.length; + while (mypos < input.length) { + var first_packet = openpgp_packet.read_packet(input, mypos, l); + // public key parser (definition from the standard:) + // OpenPGP Message :- Encrypted Message | Signed Message | + // Compressed Message | Literal Message. + // Compressed Message :- Compressed Data Packet. + // + // Literal Message :- Literal Data Packet. + // + // ESK :- Public-Key Encrypted Session Key Packet | + // Symmetric-Key Encrypted Session Key Packet. + // + // ESK Sequence :- ESK | ESK Sequence, ESK. + // + // Encrypted Data :- Symmetrically Encrypted Data Packet | + // Symmetrically Encrypted Integrity Protected Data Packet + // + // Encrypted Message :- Encrypted Data | ESK Sequence, Encrypted Data. + // + // One-Pass Signed Message :- One-Pass Signature Packet, + // OpenPGP Message, Corresponding Signature Packet. + + // Signed Message :- Signature Packet, OpenPGP Message | + // One-Pass Signed Message. + if (first_packet.tagType == 1 || + (first_packet.tagType == 2 && first_packet.signatureType < 16) || + first_packet.tagType == 3 || + first_packet.tagType == 8 || + first_packet.tagType == 9 || + first_packet.tagType == 10 || + first_packet.tagType == 11 || + first_packet.tagType == 18 || + first_packet.tagType == 19) { + messages[messages.length] = new openpgp_msg_message(); + messages[messageCount].messagePacket = first_packet; + messages[messageCount].type = dearmored.type; + // Encrypted Message + if (first_packet.tagType == 9 || + first_packet.tagType == 1 || + first_packet.tagType == 3 || + first_packet.tagType == 18) { + if (first_packet.tagType == 9) { + util.print_error("unexpected openpgp packet"); + break; + } else if (first_packet.tagType == 1) { + util.print_debug("session key found:\n "+first_packet.toString()); + var issessionkey = true; + messages[messageCount].sessionKeys = new Array(); + var sessionKeyCount = 0; + while (issessionkey) { + messages[messageCount].sessionKeys[sessionKeyCount] = first_packet; + mypos += first_packet.packetLength + first_packet.headerLength; + l -= (first_packet.packetLength + first_packet.headerLength); + first_packet = openpgp_packet.read_packet(input, mypos, l); + + if (first_packet.tagType != 1 && first_packet.tagType != 3) + issessionkey = false; + sessionKeyCount++; + } + if (first_packet.tagType == 18 || first_packet.tagType == 9) { + util.print_debug("encrypted data found:\n "+first_packet.toString()); + messages[messageCount].encryptedData = first_packet; + mypos += first_packet.packetLength+first_packet.headerLength; + l -= (first_packet.packetLength+first_packet.headerLength); + messageCount++; + + } else { + util.print_debug("something is wrong: "+first_packet.tagType); + } + + } else if (first_packet.tagType == 18) { + util.print_debug("symmetric encrypted data"); + break; + } + } else + // Signed Message + if (first_packet.tagType == 2 && first_packet.signatureType < 3) { + messages[messageCount].text = dearmored.text; + messages[messageCount].signature = first_packet; + break; + } else + // Compressed Message + // TODO: needs to be implemented. From a security perspective: this message is plaintext anyway. + if (first_packet.tagType == 8) { + util.print_error("A directly compressed message is currently not supported"); + break; + } else + // Literal Message + // TODO: needs to be implemented. From a security perspective: this message is plaintext anyway. + if (first_packet.tagType == 11) { + util.print_error("A direct literal message is currently not supported."); + break; + } + } else { + util.print_error('no message found!'); + return null; + } + } + + return messages; + } + + /** + * creates a binary string representation of an encrypted and signed message. + * The message will be encrypted with the public keys specified and signed + * with the specified private key. + * @param privatekey {obj: [openpgp_msg_privatekey]} private key to be used to sign the message + * @param publickeys [Array {obj: [openpgp_msg_publickey]}] public keys to be used to encrypt the message + * @param messagetext [String] message text to encrypt and sign + * @return [String] a binary string representation of the message which can be OpenPGP armored + */ + function write_signed_and_encrypted_message(privatekey, publickeys, messagetext) { + var result = ""; + var literal = new openpgp_packet_literaldata().write_packet(messagetext.replace(/\r\n/g,"\n").replace(/\n/g,"\r\n")); + util.print_debug("literal_packet: |"+literal+"|\n"+util.hexstrdump(literal)); + for (var i = 0; i < publickeys.length; i++) { + var onepasssignature = new openpgp_packet_onepasssignature(); + var onepasssigstr = ""; + if (i == 0) + onepasssigstr = onepasssignature.write_packet(1, openpgp.config.config.prefer_hash_algorithm, privatekey, false); + else + onepasssigstr = onepasssignature.write_packet(1, openpgp.config.config.prefer_hash_algorithm, privatekey, false); + util.print_debug("onepasssigstr: |"+onepasssigstr+"|\n"+util.hexstrdump(onepasssigstr)); + var datasignature = new openpgp_packet_signature().write_message_signature(1, messagetext.replace(/\r\n/g,"\n").replace(/\n/g,"\r\n"), privatekey); + util.print_debug("datasignature: |"+datasignature.openpgp+"|\n"+util.hexstrdump(datasignature.openpgp)); + if (i == 0) { + result = onepasssigstr+literal+datasignature.openpgp; + } else { + result = onepasssigstr+result+datasignature.openpgp; + } + } + + util.print_debug("signed packet: |"+result+"|\n"+util.hexstrdump(result)); + // signatures done.. now encryption + var sessionkey = openpgp_crypto_generateSessionKey(openpgp.config.config.encryption_cipher); + var result2 = ""; + + // creating session keys for each recipient + for (var i = 0; i < publickeys.length; i++) { + var pkey = publickeys[i].getEncryptionKey(); + if (pkey == null) { + util.print_error("no encryption key found! Key is for signing only."); + return null; + } + result2 += new openpgp_packet_encryptedsessionkey(). + write_pub_key_packet( + pkey.getKeyId(), + pkey.MPIs, + pkey.publicKeyAlgorithm, + openpgp.config.config.encryption_cipher, + sessionkey); + } + if (openpgp.config.config.integrity_protect) { + result2 += new openpgp_packet_encryptedintegrityprotecteddata().write_packet(openpgp.config.config.encryption_cipher, sessionkey, result); + } else { + result2 += new openpgp_packet_encrypteddata().write_packet(openpgp.config.config.encryption_cipher, sessionkey, result); + } + return openpgp_encoding_armor(3,result2,null,null); + } + /** + * creates a binary string representation of an encrypted message. + * The message will be encrypted with the public keys specified + * @param publickeys [Array {obj: [openpgp_msg_publickey]}] public + * keys to be used to encrypt the message + * @param messagetext [String] message text to encrypt + * @return [String] a binary string representation of the message + * which can be OpenPGP armored + */ + function write_encrypted_message(publickeys, messagetext) { + var result = ""; + var literal = new openpgp_packet_literaldata().write_packet(messagetext.replace(/\r\n/g,"\n").replace(/\n/g,"\r\n")); + util.print_debug("literal_packet: |"+literal+"|\n"+util.hexstrdump(literal)); + result = literal; + + // signatures done.. now encryption + var sessionkey = openpgp_crypto_generateSessionKey(openpgp.config.config.encryption_cipher); + var result2 = ""; + + // creating session keys for each recipient + for (var i = 0; i < publickeys.length; i++) { + var pkey = publickeys[i].getEncryptionKey(); + if (pkey == null) { + util.print_error("no encryption key found! Key is for signing only."); + return null; + } + result2 += new openpgp_packet_encryptedsessionkey(). + write_pub_key_packet( + pkey.getKeyId(), + pkey.MPIs, + pkey.publicKeyAlgorithm, + openpgp.config.config.encryption_cipher, + sessionkey); + } + if (openpgp.config.config.integrity_protect) { + result2 += new openpgp_packet_encryptedintegrityprotecteddata().write_packet(openpgp.config.config.encryption_cipher, sessionkey, result); + } else { + result2 += new openpgp_packet_encrypteddata().write_packet(openpgp.config.config.encryption_cipher, sessionkey, result); + } + return openpgp_encoding_armor(3,result2,null,null); + } + + /** + * creates a binary string representation a signed message. + * The message will be signed with the specified private key. + * @param privatekey {obj: [openpgp_msg_privatekey]} private + * key to be used to sign the message + * @param messagetext [String] message text to sign + * @return [Object: text [String], openpgp: [String] a binary + * string representation of the message which can be OpenPGP + * armored(openpgp) and a text representation of the message (text). This can be directly used to OpenPGP armor the message + */ + function write_signed_message(privatekey, messagetext) { + var sig = new openpgp_packet_signature().write_message_signature(1, messagetext.replace(/\r\n/g,"\n").replace(/\n/,"\r\n"), privatekey); + var result = {text: messagetext.replace(/\r\n/g,"\n").replace(/\n/,"\r\n"), openpgp: sig.openpgp, hash: sig.hash}; + return openpgp_encoding_armor(2,result, null, null) + } + + this.write_signed_message = write_signed_message; + this.write_signed_and_encrypted_message = write_signed_and_encrypted_message; + this.write_encrypted_message = write_encrypted_message; + this.read_message = read_message; + this.read_publicKey = read_publicKey; + this.read_privateKey = read_privateKey; + this.init = init; +} + +var openpgp = new _openpgp(); + + diff --git a/src/openpgp.keyring.js b/src/openpgp.keyring.js new file mode 100755 index 00000000..23c82ec3 --- /dev/null +++ b/src/openpgp.keyring.js @@ -0,0 +1,227 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +function openpgp_keyring() { + + /** + * Initialization routine for the keyring. This method reads the + * keyring from HTML5 local storage and initializes this instance. + * This method is called by openpgp.init(). + * @return [null] undefined + */ + function init() { + var sprivatekeys = JSON.parse(window.localStorage.getItem("privatekeys")); + var spublickeys = JSON.parse(window.localStorage.getItem("publickeys")); + if (sprivatekeys == null || sprivatekeys.length == 0) { + sprivatekeys = new Array(); + } + + if (spublickeys == null || spublickeys.length == 0) { + spublickeys = new Array(); + } + this.publicKeys = new Array(); + this.privateKeys = new Array(); + var k = 0; + for (var i =0; i < sprivatekeys.length; i++) { + var r = openpgp.read_privateKey(sprivatekeys[i]); + this.privateKeys[k] = { armored: sprivatekeys[i], obj: r[0], keyId: r[0].getKeyId()}; + k++; + } + k = 0; + for (var i =0; i < spublickeys.length; i++) { + var r = openpgp.read_publicKey(spublickeys[i]); + if (r[0] != null) { + this.publicKeys[k] = { armored: spublickeys[i], obj: r[0], keyId: r[0].getKeyId()}; + k++; + } + } + } + this.init = init; + + /** + * Checks if at least one private key is in the keyring + * @return + */ + function hasPrivateKey() { + return this.privateKeys.length > 0; + } + this.hasPrivateKey = hasPrivateKey; + + /** + * Saves the current state of the keyring to HTML5 local storage. + * The privateKeys array and publicKeys array gets Stringified using JSON + * @return [null] undefined + */ + function store() { + var priv = new Array(); + for (var i = 0; i < this.privateKeys.length; i++) { + priv[i] = this.privateKeys[i].armored; + } + var pub = new Array(); + for (var i = 0; i < this.publicKeys.length; i++) { + pub[i] = this.publicKeys[i].armored; + } + window.localStorage.setItem("privatekeys",JSON.stringify(priv)); + window.localStorage.setItem("publickeys",JSON.stringify(pub)); + } + this.store = store; + /** + * searches all public keys in the keyring matching the address or address part of the user ids + * @param email_address + * @return + */ + function getPublicKeyForAddress(email_address) { + var results = new Array(); + var spl = string.split("<"); + var email = ""; + if (spl.length > 0) { + email = spl[1].split(">")[0]; + } else { + email = string.trim(); + } + for (var i =0; i < this.publicKeys.length; i++) { + + for (var j = 0; j < this.publicKeys[i].userIds; j++) { + if (this.publicKeys[i].userIds[j].indexOf(email) >= 0) + results[results.length] = this.publicKeys[i]; + } + } + return result; + } + + /** + * Searches the keyring for a private key containing the specified email address + * @param email_address [String] email address to search for + * @return [Array[openpgp_msg_privatekey] private keys found + */ + function getPrivateKeyForAddress(email_address) { + var results = new Array(); + var spl = email_address.split("<"); + var email = ""; + if (spl.length > 1) { + email = spl[1].split(">")[0]; + } else { + email = email_address.trim(); + } + for (var i =0; i < this.privateKeys.length; i++) { + + for (var j = 0; j < this.privateKeys[i].obj.userIds.length; j++) { + if (this.privateKeys[i].obj.userIds[j].text.indexOf(email) >= 0) + results[results.length] = this.privateKeys[i]; + } + } + return results; + } + + this.getPrivateKeyForAddress = getPrivateKeyForAddress; + /** + * Searches the keyring for public keys having the specified key id + * @param keyId provided as string of hex number (lowercase) + * @return Array[openpgp_msg_privatekey] public keys found + */ + function getPublicKeysForKeyId(keyId) { + var result = new Array(); + for (var i=0; i < this.publicKeys.length; i++) + if (keyId == this.publicKeys[i].obj.getKeyId()) + result[result.length] = this.publicKeys[i]; + return result; + } + this.getPublicKeysForKeyId = getPublicKeysForKeyId; + + /** + * Searches the keyring for private keys having the specified key id + * @param keyId [String] 8 bytes as string containing the key id to look for + * @return Array[openpgp_msg_privatekey] private keys found + */ + function getPrivateKeyForKeyId(keyId) { + var result = new Array(); + for (var i=0; i < this.privateKeys.length; i++) { + if (keyId == util.hexstrdump(this.privateKeys[i].obj.getKeyId())) { + result[result.length] = { key: this.privateKeys[i], keymaterial: this.privateKeys[i].obj.privateKeyPacket}; + } + if (this.privateKeys[i].obj.subKeys != null) { + var subkeyids = this.privateKeys[i].obj.getSubKeyIds(); + for (var j=0; j < subkeyids.length; j++) + if (keyId == util.hexstrdump(subkeyids[j])) { + result[result.length] = { key: this.privateKeys[i], keymaterial: this.privateKeys[i].obj.subKeys[j]}; + } + } + } + return result; + } + this.getPrivateKeyForKeyId = getPrivateKeyForKeyId; + + /** + * Imports a public key from an exported ascii armored message + * @param armored_text [String] PUBLIC KEY BLOCK message to read the public key from + * @return [null] nothing + */ + function importPublicKey (armored_text) { + var result = openpgp.read_publicKey(armored_text); + for (var i = 0; i < result.length; i++) { + this.publicKeys[this.publicKeys.length] = {armored: armored_text, obj: result[i]}; + } + } + + /** + * Imports a private key from an exported ascii armored message + * @param armored_text [String] PRIVATE KEY BLOCK message to read the private key from + * @return [null] nothing + */ + function importPrivateKey (armored_text) { + var result = openpgp.read_privateKey(armored_text); + for (var i = 0; i < result.length; i++) { + this.privateKeys[this.privateKeys.length] = {armored: armored_text, obj: result[i]}; + } + } + + this.importPublicKey = importPublicKey; + this.importPrivateKey = importPrivateKey; + + /** + * returns the PUBLIC KEY BLOCK message representation of the public key at public key ring index + * @param index [Integer] the index of the public key within the publicKeys array + * @return [String] the PUBLIC KEY BLOCK message + */ + function exportPublicKey(index) { + return this.publicKey[index]; + } + this.exportPublicKey = exportPublicKey; + + + /** + * Removes a public key from the public key keyring at the specified index + * @param index [Integer] the index of the public key within the publicKeys array + * @return [String The public key object which has been removed + */ + function removePublicKey(index) { + var removed = this.publicKeys.splice(index,1); + this.store(); + return removed; + } + this.removePublicKey = removePublicKey; + + /** + * returns the PRIVATE KEY BLOCK message representation of the private key at private key ring index + * @param index [Integer] the index of the private key within the privateKeys array + * @return [String] the PRIVATE KEY BLOCK message + */ + function exportPrivateKey(index) { + return this.privateKeys[index]; + } + this.exportPrivateKey = exportPrivateKey; +} \ No newline at end of file diff --git a/src/openpgp.msg.message.js b/src/openpgp.msg.message.js new file mode 100755 index 00000000..34b7c57a --- /dev/null +++ b/src/openpgp.msg.message.js @@ -0,0 +1,135 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +function openpgp_msg_message() { + + // -1 = no valid passphrase submitted + // -2 = no private key found + // -3 = decryption error + // text = valid decryption + this.text = ""; + + /** + * Decrypts a message and generates user interface message out of the found. + * MDC will be verified as well as message signatures + * @param private_key [openpgp_msg_privatekey] the private the message is encrypted with (corresponding to the session key) + * @param sessionkey [openpgp_packet_encryptedsessionkey] the session key to be used to decrypt the message + * @return [String] plaintext of the message or null on error + */ + function decrypt(private_key, sessionkey) { + if (private_key == null || sessionkey == null || sessionkey == "") + return null; + var decrypted = sessionkey.decrypt(this, private_key.keymaterial); + if (decrypted == null) + return null; + var packet; + var position = 0; + var len = decrypted.length; + util.print_debug("openpgp.msg.messge decrypt:\n"+util.hexstrdump(decrypted)); + + while (position != decrypted.length && (packet = openpgp_packet.read_packet(decrypted, position, len)) != null) { + if (packet.tagType == 8) { + this.text = packet.decompress(); + decrypted = packet.decompress(); + } + util.print_debug(packet.toString()); + position += packet.headerLength+packet.packetLength; + if (position > 38) + util.print_debug("openpgp.msg.messge decrypt:\n"+util.hexstrdump(decrypted.substring(position))); + len = decrypted.length - position; + if (packet.tagType == 11) { + this.text = packet.data; + util.print_info("message successfully decrypted"); + } + if (packet.tagType == 19) + // ignore.. we checked that already in a more strict way. + continue; + if (packet.tagType == 2 && packet.signatureType < 3) { + var pubkey = openpgp.keyring.getPublicKeysForKeyId(packet.issuerKeyId); + if (pubkey.length == 0) { + util.print_warning("Unable to verify signature of issuer: "+util.hexstrdump(packet.issuerKeyId)+". Public key not found in keyring."); + } else { + if(packet.verify(this.text.replace(/\r\n/g,"\n").replace(/\n/g,"\r\n"),pubkey[0]) && pubkey[0].obj.validate()) + util.print_info("Found Good Signature from "+pubkey[0].obj.userIds[0].text+" (0x"+util.hexstrdump(pubkey[0].obj.getKeyId()).substring(8)+")"); + else + util.print_error("Signature verification failed: Bad Signature from "+pubkey[0].obj.userIds[0].text+" (0x"+util.hexstrdump(pubkey[0].obj.getKeyId()).substring(8)+")"); + + } + } + } + if (this.text == "") { + this.text = decrypted; + } + return this.text; + } + + /** + * Verifies a message signature. This function can be called after read_message if the message was signed only. + * @return [boolean] true if the signature was correct; otherwise false + */ + function verifySignature() { + var result = false; + if (this.type == 2) { + var pubkey; + if (this.signature.version == 4) { + pubkey = openpgp.keyring.getPublicKeysForKeyId(this.signature.issuerKeyId); + } else if (this.signature.version == 3) { + pubkey = openpgp.keyring.getPublicKeysForKeyId(this.signature.keyId); + } else { + util.print_error("unknown signature type on message!"); + return false; + } + if (pubkey.length == 0) + util.print_warning("Unable to verify signature of issuer: "+util.hexstrdump(this.signature.issuerKeyId)+". Public key not found in keyring."); + else { + for (var i = 0 ; i < pubkey.length; i++) { + var tohash = this.text.replace(/\r\n/g,"\n").replace(/\n/g,"\r\n"); + if (this.signature.verify(tohash.substring(0, tohash.length -2), pubkey[i])) { + util.print_info("Found Good Signature from "+pubkey[i].obj.userIds[i].text+" (0x"+util.hexstrdump(pubkey[i].obj.getKeyId()).substring(8)+")"); + result = true; + } else { + util.print_error("Signature verification failed: Bad Signature from "+pubkey[i].obj.userIds[0].text+" (0x"+util.hexstrdump(pubkey[0].obj.getKeyId()).substring(8)+")"); + } + } + } + } + return result; + } + + function toString() { + var result = "Session Keys:\n"; + if (this.sessionKeys !=null) + for (var i = 0; i < this.sessionKeys.length; i++) { + result += this.sessionKeys[i].toString(); + } + result += "\n\n EncryptedData:\n"; + if(this.encryptedData != null) + result += this.encryptedData.toString(); + + result += "\n\n Signature:\n"; + if(this.signature != null) + result += this.signature.toString(); + + result += "\n\n Text:\n" + if(this.signature != null) + result += this.text; + return result; + } + this.decrypt = decrypt; + this.verifySignature = verifySignature; + this.toString = toString; +} \ No newline at end of file diff --git a/src/openpgp.msg.privatekey.js b/src/openpgp.msg.privatekey.js new file mode 100755 index 00000000..4d3222d1 --- /dev/null +++ b/src/openpgp.msg.privatekey.js @@ -0,0 +1,144 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +function openpgp_msg_privatekey() { + this.subKeys = new Array(); + this.privateKeyPacket = null; + this.userIds = new Array(); + this.userAttributes = new Array(); + this.revocationSignatures = new Array(); + this.subKeys = new Array(); + + /** + * + * @return last position + */ + function read_nodes(parent_node, input, position, len) { + this.privateKeyPacket = parent_node; + + var pos = position; + while (input.length > pos) { + var result = openpgp_packet.read_packet(input, pos, input.length - pos); + if (result == null) { + util.print_error("openpgp.msg.messge decrypt:\n"+'[pub/priv_key]parsing ends here @:' + pos + " l:" + len); + break; + } else { + switch (result.tagType) { + case 2: // public key revocation signature + if (result.signatureType == 32) + this.revocationSignatures[this.revocationSignatures.length] = result; + else if (result.signatureType > 15 && result.signatureType < 20) { + if (this.certificationsignatures == null) + this.certificationSignatures = new Array(); + this.certificationSignatures[this.certificationSignatures.length] = result; + } else + util.print_error("openpgp.msg.messge decrypt:\n"+"unknown signature type directly on key "+result.signatureType+" @"+pos); + pos += result.packetLength + result.headerLength; + break; + case 7: // PrivateSubkey Packet + this.subKeys[this.subKeys.length] = result; + pos += result.packetLength + result.headerLength; + pos += result.read_nodes(this.privateKeyPacket,input, pos, input.length - pos); + break; + case 17: // User Attribute Packet + this.userAttributes[this.userAttributes.length] = result; + pos += result.packetLength + result.headerLength; + pos += result.read_nodes(this.privateKeyPacket,input, pos, input.length - pos); + break; + case 13: // User ID Packet + this.userIds[this.userIds.length] = result; + pos += result.packetLength + result.headerLength; + pos += result.read_nodes(this.privateKeyPacket, input, pos, input.length - pos); + break; + default: + this.position = position - this.privateKeyPacket.packetLength - this.privateKeyPacket.headerLength; + this.len = pos - position; + return this.len; + } + } + } + this.position = position - this.privateKeyPacket.packetLength - this.privateKeyPacket.headerLength; + this.len = pos - position; + + return this.len; + } + + function getKeyId() { + return this.privateKeyPacket.publicKey.getKeyId(); + } + + + function getSubKeyIds() { + if (this.privateKeyPacket.publicKey.version == 4) // V3 keys MUST NOT have subkeys. + var result = new Array(); + for (var i = 0; i < this.subKeys.length; i++) { + result[i] = str_sha1(this.subKeys[i].publicKey.header+this.subKeys[i].publicKey.data).substring(12,20); + } + return result; + } + + + function getSigningKey() { + if ((this.privateKeyPacket.publicKey.publicKeyAlgorithm == 17 || + this.privateKeyPacket.publicKey.publicKeyAlgorithm != 2) + && this.privateKeyPacket.publicKey.verifyKey() == 3) + return this.privateKeyPacket; + else if (this.privateKeyPacket.publicKey.version == 4) // V3 keys MUST NOT have subkeys. + for (var j = 0; j < this.privateKeyPacket.subKeys.length; j++) { + if ((this.privateKeyPacket.subKeys[j].publicKey.publicKeyAlgorithm == 17 || + this.privateKeyPacket.subKeys[j].publicKey.publicKeyAlgorithm != 2) && + this.privateKeyPacket.subKeys[j].publicKey.verifyKey() == 3) + return this.privateKeyPacket.subKeys[j]; + } + return null; + } + + function getPreferredSignatureHashAlgorithm() { + var pkey = this.getSigningKey(); + if (pkey == null) { + util.print_error("private key is for encryption only! Cannot create a signature.") + return null; + } + if (pkey.publicKey.publicKeyAlgorithm == 17) { + var dsa = new DSA(); + return dsa.select_hash_algorithm(pkey.publicKey.MPIs[1].toBigInteger()); // q + } + return openpgp.config.config.prefer_hash_algorithm; + + } + + function decryptSecretMPIs(str_passphrase) { + return this.privateKeyPacket.decryptSecretMPIs(str_passphrase); + } + + function getFingerprint() { + return this.privateKeyPacket.publicKey.getFingerprint(); + } + + // TODO need to implement this + function revoke() { + + } + this.getSigningKey = getSigningKey; + this.getFingerprint = getFingerprint; + this.getPreferredSignatureHashAlgorithm = getPreferredSignatureHashAlgorithm; + this.read_nodes = read_nodes; + this.decryptSecretMPIs = decryptSecretMPIs; + this.getSubKeyIds = getSubKeyIds; + this.getKeyId = getKeyId; + +} \ No newline at end of file diff --git a/src/openpgp.msg.publickey.js b/src/openpgp.msg.publickey.js new file mode 100755 index 00000000..b24924bf --- /dev/null +++ b/src/openpgp.msg.publickey.js @@ -0,0 +1,252 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +function openpgp_msg_publickey() { + this.data; + this.position; + this.len; + this.tostring = "OPENPGP PUBLIC KEY\n"; + this.bindingSignature = null; + this.publicKeyPacket = null; + this.userIds = new Array(); + this.userAttributes = new Array(); + this.revocationSignatures = new Array(); + this.subKeys = new Array(); + this.arbitraryPacket = new Array(); + this.directSignatures = new Array(); + /** + * + * @return last position + */ + function read_nodes(parent_node, input, position, len) { + this.publicKeyPacket = parent_node; + var exit = false; + var pos = position; + var l = len; + while (input.length != pos) { + var result = openpgp_packet.read_packet(input, pos, input.length - pos); + if (result == null) { + util.print_error("openpgp.msg.publickey read_nodes:\n"+'[pub_key]parsing ends here @:' + pos + " l:" + l); + break; + } else { + switch (result.tagType) { + case 2: // public key revocation signature + if (result.signatureType == 32) + this.revocationSignatures[this.revocationSignatures.length] = result; + else if (result.signatureType == 19) + this.certificationSignature = result; + else if (result.signatureType == 25) { + this.bindingSignature = result; + } else if (result.signatureType == 31) { + this.directSignatures[this.directSignatures.length] = result; + } else + util.print_error("openpgp.msg.publickey read_nodes:\n"+"unknown signature type directly on key "+result.signatureType); + pos += result.packetLength + result.headerLength; + break; + case 14: // Public-Subkey Packet + this.subKeys[this.subKeys.length] = result; + pos += result.packetLength + result.headerLength; + pos += result.read_nodes(this.publicKeyPacket,input, pos, input.length - pos); + break; + case 17: // User Attribute Packet + this.userAttributes[this.userAttributes.length] = result; + pos += result.packetLength + result.headerLength; + pos += result.read_nodes(this.publicKeyPacket,input, pos, input.length - pos); + break; + case 13: // User ID Packet + this.userIds[this.userIds.length] = result; + pos += result.packetLength + result.headerLength; + pos += result.read_nodes(this.publicKeyPacket, input, pos, input.length - pos); + break; + default: + this.data = input; + this.position = position - this.publicKeyPacket.packetLength - this.publicKeyPacket.headerLength; + this.len = pos - position; + return this.len; + } + } + } + this.data = input; + this.position = position - (this.publicKeyPacket.packetLength - this.publicKeyPacket.headerLength); + this.len = pos - position; + return this.len; + } + + function write() { + + } + + function getKeyId() { + return this.publicKeyPacket.getKeyId(); + } + + function getFingerprint() { + return this.publicKeyPacket.getFingerprint(); + } + + + + function validate() { + // check revocation keys + for (var i = 0; i < this.revocationSignatures.length; i++) { + var tohash = this.publicKeyPacket.header+this.publicKeyPacket.data; + if (this.revocationSignatures[i].verify(tohash, this.publicKeyPacket)) + return false; + } + + if (this.subKeys.length != 0) { + // search for a valid subkey + var found = false; + for (var i = 0; i < this.subKeys.length; i++) + if (this.subKeys[i].verifyKey() == 3) { + found = true; + break; + } + if (!found) + return false; + } + // search for one valid userid + found = false; + for (var i = 0; i < this.userIds.length; i++) + if (this.userIds[i].verify(this.publicKeyPacket) == 0) { + found = true; + break; + } + if (!found) + return false; + return true; + } + + /** + * verifies all signatures + * @return a 2 dimensional array. the first dimension corresponds to the userids available + */ + function verifyCertificationSignatures() { + var result = new Array(); + for (var i = 0; i < this.userIds.length; i++) { + result[i] = this.userIds[i].verifyCertificationSignatures(this.publicKeyPacket); + } + return result; + } + this.verifyCertificationSignatures = verifyCertificationSignatures; + + /** + * verifies: + * - revocation certificates directly on key + * - self signatures + * - subkey binding and revocation certificates + * + * This is useful for validating the key + * @returns true if the basic signatures are all valid + */ + function verifyBasicSignatures() { + for (var i = 0; i < this.revocationSignatures.length; i++) { + var tohash = this.publicKeyPacket.header+this.publicKeyPacket.data; + if (this.revocationSignatures[i].verify(tohash, this.publicKeyPacket)) + return false; + else return false; + } + + if (this.subKeys.length != 0) { + // search for a valid subkey + var found = false; + for (var i = 0; i < this.subKeys.length; i++) { + if (this.subKeys[i] == null) + continue; + var result = this.subKeys[i].verifyKey(); + if (result == 3) { + found = true; + break; + } + } + if (!found) + return false; + } + var keyId = this.getKeyId(); + for (var i = 0; i < this.userIds.length; i++) { + for (var j = 0; j < this.userIds[i].certificationRevocationSignatures.length; j++) { + if (this.userIds[i].certificationSignatures[j].getIssuer == keyId && + this.userIds[i].certificationSignatures[j].verifyBasic(this.publicKeyPacket) != 4) + return false; + } + } + return true; + } + + function toString() { + var result = " OPENPGP Public Key\n length: "+this.len+"\n"; + result += " Revocation Signatures:\n" + for (var i=0; i < this.revocationSignatures.length; i++) { + result += " "+this.revocationSignatures[i].toString(); + } + result += " User Ids:\n"; + for (var i=0; i < this.userIds.length; i++) { + result += " "+this.userIds[i].toString(); + } + result += " User Attributes:\n"; + for (var i=0; i < this.userAttributes.length; i++) { + result += " "+this.userAttributes[i].toString(); + } + result += " Public Key SubKeys:\n"; + for (var i=0; i < this.subKeys.length; i++) { + result += " "+this.subKeys[i].toString(); + } + return result; + } + + /** + * finds an encryption key for this public key + * @returns null if no encryption key has been found + */ + function getEncryptionKey() { + if (this.publicKeyPacket.publicKeyAlgorithm != 17 && this.publicKeyPacket.publicKeyAlgorithm != 3 + && this.publicKeyPacket.verifyKey()) + return this.publicKeyPacket; + else if (this.publicKeyPacket.version == 4) // V3 keys MUST NOT have subkeys. + for (var j = 0; j < this.subKeys.length; j++) + if (this.subKeys[j].publicKeyAlgorithm != 17 && + this.subKeys[j].publicKeyAlgorithm != 3 && + this.subKeys[j].verifyKey()) { + return this.subKeys[j]; + } + return null; + } + + function getSigningKey() { + if ((this.publicKeyPacket.publicKeyAlgorithm == 17 || + this.publicKeyPacket.publicKeyAlgorithm != 2)) + return this.publicKeyPacket; + else if (this.publicKeyPacket.version == 4) // V3 keys MUST NOT have subkeys. + for (var j = 0; j < this.subKeys.length; j++) { + if ((this.subKeys[j].publicKeyAlgorithm == 17 || + this.subKeys[j].publicKeyAlgorithm != 2) && + this.subKeys[j].verifyKey()) + return this.subKeys[j]; + } + return null; + } + + this.getEncryptionKey = getEncryptionKey; + this.getSigningKey = getSigningKey; + this.read_nodes = read_nodes; + this.write = write; + this.toString = toString; + this.validate = validate; + this.getFingerprint = getFingerprint; + this.getKeyId = getKeyId; + this.verifyBasicSignatures = verifyBasicSignatures; +} \ No newline at end of file diff --git a/src/packet/openpgp.packet.compressed.js b/src/packet/openpgp.packet.compressed.js new file mode 100755 index 00000000..97c622e7 --- /dev/null +++ b/src/packet/openpgp.packet.compressed.js @@ -0,0 +1,145 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * Implementation of the Compressed Data Packet (Tag 8) + * + * RFC4880 5.6: + * The Compressed Data packet contains compressed data. Typically, this + * packet is found as the contents of an encrypted packet, or following + * a Signature or One-Pass Signature packet, and contains a literal data + * packet. + */ +function openpgp_packet_compressed() { + this.tagType = 8; + + /** + * parsing function for the packet. + * @param input [string] payload of a tag 8 packet + * @param position [integer] position to start reading from the input string + * @param len [integer] length of the packet or the remaining length of input at position + * @return [openpgp_packet_compressed] object representation + */ + function read_packet (input, position, len) { + this.packetLength = len; + var mypos = position; + // One octet that gives the algorithm used to compress the packet. + this.type = input.charCodeAt(mypos++); + // Compressed data, which makes up the remainder of the packet. + this.compressedData = input.substring(position+1, position+len); + return this; + } + /** + * decompression method for decompressing the compressed data + * read by read_packet + * @return [String] the decompressed data + */ + function decompress() { + if (this.decompressedData != null) + return this.decompressedData; + + if (this.type == null) + return null; + + switch (this.type) { + case 0: // - Uncompressed + this.decompressedData = this.compressedData; + break; + case 1: // - ZIP [RFC1951] + util.print_error("Compression algorithm ZIP [RFC1951] is not implemented."); + break; + case 2: // - ZLIB [RFC1950] + // TODO: need to implement this + this.decompressedData = JXG.decompress(s2r(this.compressedData).replace(/\n/g,"")); + util.print_error("Compression algorithm ZLIB [RFC1950] is not implemented."); + break; + case 3: // - BZip2 [BZ2] + // TODO: need to implement this + util.print_error("Compression algorithm BZip2 [BZ2] is not implemented."); + break; + default: + util.print_error("Compression algorithm unknown :"+this.type); + break; + } + util.print_debug("decompressed:"+util.hexstrdump(this.decompressedData)); + return this.decompressedData; + } + + /** + * Compress the packet data (member decompressedData) + * @param type [integer] algorithm to be used // See RFC 4880 9.3 + * @param data [String] data to be compressed + * @return [String] The compressed data stored in attribute compressedData + */ + function compress(type, data) { + this.type = type; + this.decompressedData = data; + switch (this.type) { + case 0: // - Uncompressed + this.compressedData = this.decompressedData; + break; + case 1: // - ZIP [RFC1951] + util.print_error("Compression algorithm ZIP [RFC1951] is not implemented."); + break; + case 2: // - ZLIB [RFC1950] + // TODO: need to implement this + util.print_error("Compression algorithm ZLIB [RFC1950] is not implemented."); + break; + case 3: // - BZip2 [BZ2] + // TODO: need to implement this + util.print_error("Compression algorithm BZip2 [BZ2] is not implemented."); + break; + default: + util.print_error("Compression algorithm unknown :"+this.type); + break; + } + this.packetLength = this.compressedData.length +1; + return this.compressedData; + } + + /** + * creates a string representation of the packet + * @param algorithm [integer] algorithm to be used // See RFC 4880 9.3 + * @param data [String] data to be compressed + * @return [String] string-representation of the packet + */ + function write_packet(algorithm, data) { + this.decompressedData = data; + if (algorithm == null) { + this.type = 1; + } + var result = String.fromCharCode(this.type)+this.compress(this.type); + return openpgp_packet.write_packet_header(8, result.length)+result; + } + + /** + * pretty printing the packet (useful for debug purposes) + * @return [String] + */ + function toString() { + return '5.6. Compressed Data Packet (Tag 8)\n'+ + ' length: '+this.packetLength+'\n'+ + ' Compression Algorithm = '+this.type+'\n'+ + ' Compressed Data: Byte ['+util.hexstrdump(this.compressedData)+']\n'; + } + + this.read_packet = read_packet; + this.toString = toString; + this.compress = compress; + this.decompress = decompress; + this.write_packet = write_packet; +}; diff --git a/src/packet/openpgp.packet.encrypteddata.js b/src/packet/openpgp.packet.encrypteddata.js new file mode 100755 index 00000000..dfd69066 --- /dev/null +++ b/src/packet/openpgp.packet.encrypteddata.js @@ -0,0 +1,104 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * Implementation of the Symmetrically Encrypted Data Packet (Tag 9) + * + * RFC4880 5.7: The Symmetrically Encrypted Data packet contains data encrypted + * with a symmetric-key algorithm. When it has been decrypted, it contains other + * packets (usually a literal data packet or compressed data packet, but in + * theory other Symmetrically Encrypted Data packets or sequences of packets + * that form whole OpenPGP messages). + */ + +function openpgp_packet_encrypteddata() { + this.tagType = 9; + this.packetLength = null; + this.encryptedData = null; + this.decryptedData = null; + + /** + * parsing function for the packet. + * + * @param input + * [string] payload of a tag 9 packet + * @param position + * [integer] position to start reading from the input string + * @param len + * [integer] length of the packet or the remaining length of + * input at position + * @return [openpgp_packet_encrypteddata] object representation + */ + function read_packet(input, position, len) { + var mypos = position; + this.packetLength = len; + // - Encrypted data, the output of the selected symmetric-key cipher + // operating in OpenPGP's variant of Cipher Feedback (CFB) mode. + this.encryptedData = input.substring(position, position + len); + return this; + } + + /** + * symmetrically decrypt the packet data + * + * @param symmetric_algorithm_type + * [integer] symmetric key algorithm to use // See RFC4880 9.2 + * @param key + * [String] key as string with the corresponding length to the + * algorithm + * @return the decrypted data; + */ + function decrypt_sym(symmetric_algorithm_type, key) { + this.decryptedData = openpgp_crypto_symmetricDecrypt( + symmetric_algorithm_type, key, this.encryptedData, true); + util.print_debug("openpgp.packet.encryptedintegrityprotecteddata.js\n"+ + "data: "+util.hexstrdump(this.decryptedData)); + return this.decryptedData; + } + + /** + * Creates a string representation of the packet + * + * @param algo + * [Integer] symmetric key algorithm to use // See RFC4880 9.2 + * @param key + * [String] key as string with the corresponding length to the + * algorithm + * @param data + * [String] data to be + * @return [String] string-representation of the packet + */ + function write_packet(algo, key, data) { + var result = ""; + result += openpgp_crypto_symmetricEncrypt( + openpgp_crypto_getPrefixRandom(algo), algo, key, data, true); + result = openpgp_packet.write_packet_header(9, result.length) + result; + return result; + } + + function toString() { + return '5.7. Symmetrically Encrypted Data Packet (Tag 9)\n' + + ' length: ' + this.packetLength + '\n' + + ' Used symmetric algorithm: ' + this.algorithmType + '\n' + + ' encrypted data: Bytes [' + + util.hexstrdump(this.encryptedData) + ']\n'; + } + this.decrypt_sym = decrypt_sym; + this.toString = toString; + this.read_packet = read_packet; + this.write_packet = write_packet; +}; diff --git a/src/packet/openpgp.packet.encryptedintegrityprotecteddata.js b/src/packet/openpgp.packet.encryptedintegrityprotecteddata.js new file mode 100755 index 00000000..135267af --- /dev/null +++ b/src/packet/openpgp.packet.encryptedintegrityprotecteddata.js @@ -0,0 +1,155 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * Implementation of the Sym. Encrypted Integrity Protected Data Packet (Tag 18) + * + * RFC4880 5.13: The Symmetrically Encrypted Integrity Protected Data packet is + * a variant of the Symmetrically Encrypted Data packet. It is a new feature + * created for OpenPGP that addresses the problem of detecting a modification to + * encrypted data. It is used in combination with a Modification Detection Code + * packet. + */ + +function openpgp_packet_encryptedintegrityprotecteddata() { + this.tagType = 18; + this.version = null; // integer == 1 + this.packetLength = null; // integer + this.encryptedData = null; // string + this.decrytpedData = null; // string + this.hash = null; // string + /** + * parsing function for the packet. + * + * @param input + * [string] payload of a tag 18 packet + * @param position + * [integer] position to start reading from the input string + * @param len + * [integer] length of the packet or the remaining length of + * input at position + * @return [openpgp_packet_encryptedintegrityprotecteddata] object + * representation + */ + function read_packet(input, position, len) { + this.packetLength = len; + // - A one-octet version number. The only currently defined value is + // 1. + this.version = input[position].charCodeAt(); + if (this.version != 1) { + util + .print_error('openpgp.packet.encryptedintegrityprotecteddata.js\nunknown encrypted integrity protected data packet version: ' + + this.version + + " , @ " + + position + + "hex:" + + util.hexstrdump(input)); + return null; + } + // - Encrypted data, the output of the selected symmetric-key cipher + // operating in Cipher Feedback mode with shift amount equal to the + // block size of the cipher (CFB-n where n is the block size). + this.encryptedData = input.substring(position + 1, position + 1 + len); + util.print_debug("openpgp.packet.encryptedintegrityprotecteddata.js\n" + + this.toString()); + return this; + } + + /** + * Creates a string representation of a Sym. Encrypted Integrity Protected + * Data Packet (tag 18) (see RFC4880 5.13) + * + * @param symmetric_algorithm + * [integer] the selected symmetric encryption algorithm to be + * used + * @param key + * [String] the key of cipher blocksize length to be used + * @param data + * plaintext data to be encrypted within the packet + * @return a string representation of the packet + */ + function write_packet(symmetric_algorithm, key, data) { + + var prefixrandom = openpgp_crypto_getPrefixRandom(symmetric_algorithm); + var prefix = prefixrandom + + prefixrandom.charAt(prefixrandom.length - 2) + + prefixrandom.charAt(prefixrandom.length - 1); + var tohash = data; + tohash += String.fromCharCode(0xD3); + tohash += String.fromCharCode(0x14); + util.print_debug("data to be hashed:" + + util.hexstrdump(prefix + tohash)); + tohash += str_sha1(prefix + tohash); + util.print_debug("hash:" + + util.hexstrdump(tohash.substring(tohash.length - 20, + tohash.length))); + var result = openpgp_crypto_symmetricEncrypt(prefixrandom, + symmetric_algorithm, key, tohash, false).substring(0, + prefix.length + tohash.length); + var header = openpgp_packet.write_packet_header(18, result.length + 1) + + String.fromCharCode(1); + this.encryptedData = result; + return header + result; + } + + /** + * Decrypts the encrypted data contained in this object read_packet must + * have been called before + * + * @param symmetric_algorithm_type + * [integer] the selected symmetric encryption algorithm to be + * used + * @param key + * [String] the key of cipher blocksize length to be used + * @return the decrypted data of this packet + */ + function decrypt(symmetric_algorithm_type, key) { + this.decryptedData = openpgp_crypto_symmetricDecrypt( + symmetric_algorithm_type, key, this.encryptedData, false); + // there must be a modification detection code packet as the + // last packet and everything gets hashed except the hash itself + this.hash = str_sha1(openpgp_crypto_MDCSystemBytes( + symmetric_algorithm_type, key, this.encryptedData) + + this.decryptedData.substring(0, + this.decryptedData.length - 20)); + util.print_debug("calc hash = " + util.hexstrdump(this.hash)); + if (this.hash == this.decryptedData.substring( + this.decryptedData.length - 20, this.decryptedData.length)) + return this.decryptedData; + else + util + .print_error("Decryption stopped: discovered a modification of encrypted data."); + return null; + } + + function toString() { + return '5.13. Sym. Encrypted Integrity Protected Data Packet (Tag 18)\n' + + ' length: ' + + this.packetLength + + '\n' + + ' version: ' + + this.version + + '\n' + + ' data: Bytes [' + + util.hexstrdump(this.encryptedData) + ']'; + } + + this.write_packet = write_packet; + this.read_packet = read_packet; + this.toString = toString; + this.decrypt = decrypt; +}; \ No newline at end of file diff --git a/src/packet/openpgp.packet.encryptedsessionkey.js b/src/packet/openpgp.packet.encryptedsessionkey.js new file mode 100755 index 00000000..e75365cf --- /dev/null +++ b/src/packet/openpgp.packet.encryptedsessionkey.js @@ -0,0 +1,242 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * Public-Key Encrypted Session Key Packets (Tag 1) + * + * RFC4880 5.1: A Public-Key Encrypted Session Key packet holds the session key + * used to encrypt a message. Zero or more Public-Key Encrypted Session Key + * packets and/or Symmetric-Key Encrypted Session Key packets may precede a + * Symmetrically Encrypted Data Packet, which holds an encrypted message. The + * message is encrypted with the session key, and the session key is itself + * encrypted and stored in the Encrypted Session Key packet(s). The + * Symmetrically Encrypted Data Packet is preceded by one Public-Key Encrypted + * Session Key packet for each OpenPGP key to which the message is encrypted. + * The recipient of the message finds a session key that is encrypted to their + * public key, decrypts the session key, and then uses the session key to + * decrypt the message. + */ +function openpgp_packet_encryptedsessionkey() { + + /** + * parsing function for a publickey encrypted session key packet (tag 1). + * + * @param input + * [string] payload of a tag 1 packet + * @param position + * [integer] position to start reading from the input string + * @param len + * [integer] length of the packet or the remaining length of + * input at position + * @return [openpgp_packet_encrypteddata] object representation + */ + function read_pub_key_packet(input, position, len) { + this.tagType = 1; + this.packetLength = len; + var mypos = position; + if (len < 10) { + util + .print_error("openpgp.packet.encryptedsessionkey.js\n" + 'invalid length'); + return null; + } + + this.version = input[mypos++].charCodeAt(); + this.keyId = new openpgp_type_keyid(); + this.keyId.read_packet(input, mypos); + mypos += 8; + this.publicKeyAlgorithmUsed = input[mypos++].charCodeAt(); + + switch (this.publicKeyAlgorithmUsed) { + case 1: + case 2: // RSA + this.MPIs = new Array(); + this.MPIs[0] = new openpgp_type_mpi(); + this.MPIs[0].read(input, mypos, mypos - position); + break; + case 16: // Elgamal + this.MPIs = new Array(); + this.MPIs[0] = new openpgp_type_mpi(); + this.MPIs[0].read(input, mypos, mypos - position); + mypos += this.MPIs[0].packetLength; + this.MPIs[1] = new openpgp_type_mpi(); + this.MPIs[1].read(input, mypos, mypos - position); + break; + default: + util.print_error("openpgp.packet.encryptedsessionkey.js\n" + + "unknown public key packet algorithm type " + + this.publicKeyAlgorithmType); + break; + } + return this; + } + + /** + * create a string representation of a tag 1 packet + * + * @param publicKeyId + * [String] the public key id corresponding to publicMPIs key as + * string + * @param publicMPIs + * [Array[openpgp_type_mpi]] multiprecision integer objects + * describing the public key + * @param pubalgo + * [integer] the corresponding public key algorithm // See + * RFC4880 9.1 + * @param symmalgo + * [integer] the symmetric cipher algorithm used to encrypt the + * data within an encrypteddatapacket or + * encryptedintegrityprotecteddatapacket following this packet // + * See RFC4880 9.2 + * @param sessionkey + * [String] a string of randombytes representing the session key + * @return [String] the string representation + */ + function write_pub_key_packet(publicKeyId, publicMPIs, pubalgo, symmalgo, + sessionkey) { + var result = String.fromCharCode(3); + var data = String.fromCharCode(symmalgo); + data += sessionkey; + var checksum = util.calc_checksum(sessionkey); + data += String.fromCharCode((checksum >> 8) & 0xFF); + data += String.fromCharCode((checksum) & 0xFF); + result += publicKeyId; + result += String.fromCharCode(pubalgo); + var mpi = new openpgp_type_mpi(); + var mpiresult = openpgp_crypto_asymetricEncrypt(pubalgo, publicMPIs, + mpi.create(openpgp_encoding_eme_pkcs1_encode(data, + publicMPIs[0].mpiByteLength))); + for ( var i = 0; i < mpiresult.length; i++) { + result += mpiresult[i]; + } + result = openpgp_packet.write_packet_header(1, result.length) + result; + return result; + } + + /** + * parsing function for a symmetric encrypted session key packet (tag 3). + * + * @param input + * [string] payload of a tag 1 packet + * @param position + * [integer] position to start reading from the input string + * @param len + * [integer] length of the packet or the remaining length of + * input at position + * @return [openpgp_packet_encrypteddata] object representation + */ + function read_symmetric_key_packet(input, position, len) { + this.tagType = 3; + var mypos = position; + // A one-octet version number. The only currently defined version is 4. + this.version = input[mypos++]; + + // A one-octet number describing the symmetric algorithm used. + this.symmetricKeyAlgorithmUsed = input[mypos++]; + // A string-to-key (S2K) specifier, length as defined above. + this.s2k = new openpgp_type_s2k(); + this.s2k.read(input, mypos); + + // Optionally, the encrypted session key itself, which is decrypted + // with the string-to-key object. + if ((s2k.s2kLength + mypos) < len) { + this.encryptedSessionKey = new Array(); + for ( var i = (mypos - position); i < len; i++) { + this.encryptedSessionKey[i] = input[mypos++]; + } + } + return this; + } + /** + * Decrypts the session key (only for public key encrypted session key + * packets (tag 1) + * + * @param msg + * [openpgp_msg_message] the message object (with member + * encryptedData + * @param key + * [openpgp_msg_privatekey] private key with secMPIs unlocked + * @return [String] the unencrypted session key + */ + function decrypt(msg, key) { + if (this.tagType == 1) { + var result = openpgp_crypto_asymetricDecrypt( + this.publicKeyAlgorithmUsed, key.publicKey.MPIs, + key.secMPIs, this.MPIs).toMPI(); + var checksum = ((result.charCodeAt(result.length - 2) << 8) + result + .charCodeAt(result.length - 1)); + // search for zero + // FIXME: this is a poor way to decode a padding + var i = 0; + while (result.charCodeAt(i) != 0 && i < result.length) + i++; + i++; + if (i > result.length) { + return null; + } + var algo = result.charCodeAt(i++); + var sesskey = result.substring(i, result.length - 2); + if (msg.encryptedData.tagType == 18) + return msg.encryptedData.decrypt(algo, sesskey); + else + return msg.encryptedData.decrypt_sym(algo, sesskey); + } else if (this.tagType == 3) { + util + .print_error("Symmetric encrypted sessionkey is not supported!"); + return null; + } + } + + /** + * Creates a string representation of this object (useful for debug + * purposes) + * + * @return the string containing a openpgp description + */ + function toString() { + if (this.tagType == 1) { + var result = '5.1. Public-Key Encrypted Session Key Packets (Tag 1)\n' + + ' KeyId: ' + + this.keyId.toString() + + '\n' + + ' length: ' + + this.packetLength + + '\n' + + ' version:' + + this.version + + '\n' + + ' pubAlgUs:' + + this.publicKeyAlgorithmUsed + '\n'; + for ( var i = 0; i < this.MPIs.length; i++) { + result += this.MPIs[i].toString(); + } + return result; + } else + return '5.3 Symmetric-Key Encrypted Session Key Packets (Tag 3)\n' + + ' KeyId: ' + this.keyId.toString() + '\n' + + ' length: ' + this.packetLength + '\n' + + ' version:' + this.version + '\n' + ' symKeyA:' + + this.symmetricKeyAlgorithmUsed + '\n' + ' s2k: ' + + this.s2k + '\n'; + } + + this.read_pub_key_packet = read_pub_key_packet; + this.read_symmetric_key_packet = read_symmetric_key_packet; + this.write_pub_key_packet = write_pub_key_packet; + this.toString = toString; + this.decrypt = decrypt; +}; + diff --git a/src/packet/openpgp.packet.js b/src/packet/openpgp.packet.js new file mode 100755 index 00000000..05d68973 --- /dev/null +++ b/src/packet/openpgp.packet.js @@ -0,0 +1,409 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +function _openpgp_packet() { + /** + * Encodes a given integer of length to the openpgp length specifier to a + * string + * + * @param length + * [Integer] of the length to encode + * @return string with openpgp length representation + */ + function encode_length(length) { + result = ""; + if (length < 192) { + result += String.fromCharCode(length); + } else if (length > 191 && length < 8384) { + /* + * let a = (total data packet length) - 192 let bc = two octet + * representation of a let d = b + 192 + */ + result += String.fromCharCode(((length - 192) >> 8) + 192); + result += String.fromCharCode((length - 192) & 0xFF); + } else { + result += String.fromCharCode(255); + result += String.fromCharCode((length >> 24) & 0xFF); + result += String.fromCharCode((length >> 16) & 0xFF); + result += String.fromCharCode((length >> 8) & 0xFF); + result += String.fromCharCode(length & 0xFF); + } + return result; + } + this.encode_length = encode_length; + + /** + * Writes a packet header version 4 with the given tag_type and length to a + * string + * + * @param tag_type + * integer of tag type + * @param length + * integer length of the payload + * @return string of the header + */ + function write_packet_header(tag_type, length) { + /* we're only generating v4 packet headers here */ + var result = ""; + result += String.fromCharCode(0xC0 | tag_type); + result += encode_length(length); + return result; + } + + /** + * Writes a packet header Version 3 with the given tag_type and length to a + * string + * + * @param tag_type + * integer of tag type + * @param length + * integer length of the payload + * @return string of the header + */ + function write_old_packet_header(tag_type, length) { + var result = ""; + if (length < 256) { + result += String.fromCharCode(0x80 | (tag_type << 2)); + result += String.fromCharCode(length); + } else if (length < 65536) { + result += String.fromCharCode(0x80 | (tag_type << 2) | 1); + result += String.fromCharCode(length >> 8); + result += String.fromCharCode(length & 0xFF); + } else { + result += String.fromCharCode(0x80 | (tag_type << 2) | 2); + result += String.fromCharCode((length >> 24) & 0xFF); + result += String.fromCharCode((length >> 16) & 0xFF); + result += String.fromCharCode((length >> 8) & 0xFF); + result += String.fromCharCode(length & 0xFF); + } + return result; + } + this.write_old_packet_header = write_old_packet_header; + this.write_packet_header = write_packet_header; + /** + * Generic static Packet Parser function + * + * @param input + * [String] input stream as string + * @param position + * [integer] position to start parsing + * @param len + * [integer] length of the input from position on + * @return [openpgp_packet_*] returns a parsed openpgp_packet + */ + function read_packet(input, position, len) { + // some sanity checks + if (input == null || input.length <= position + || input.substring(position).length < 2 + || (input[position].charCodeAt() & 0x80) == 0) { + util + .print_error("Error during parsing. This message / key is propably not containing a valid OpenPGP format."); + return null; + } + var mypos = position; + var tag = -1; + var format = -1; + + format = 0; // 0 = old format; 1 = new format + if ((input[mypos].charCodeAt() & 0x40) != 0) { + format = 1; + } + + var packet_length_type; + if (format) { + // new format header + tag = input[mypos].charCodeAt() & 0x3F; // bit 5-0 + } else { + // old format header + tag = (input[mypos].charCodeAt() & 0x3F) >> 2; // bit 5-2 + packet_length_type = input[mypos].charCodeAt() & 0x03; // bit 1-0 + } + + // header octet parsing done + mypos++; + + // parsed length from length field + var len = 0; + var bodydata = null; + + // used for partial body lengths + var real_packet_length = -1; + if (!format) { + // 4.2.1. Old Format Packet Lengths + switch (packet_length_type) { + case 0: // The packet has a one-octet length. The header is 2 octets + // long. + packet_length = input[mypos++].charCodeAt(); + break; + case 1: // The packet has a two-octet length. The header is 3 octets + // long. + packet_length = (input[mypos++].charCodeAt() << 8) + | input[mypos++].charCodeAt(); + break; + case 2: // The packet has a four-octet length. The header is 5 + // octets long. + packet_length = (input[mypos++].charCodeAt() << 24) + | (input[mypos++].charCodeAt() << 16) + | (input[mypos++].charCodeAt() << 8) + | input[mypos++].charCodeAt(); + break; + default: + // 3 - The packet is of indeterminate length. The header is 1 + // octet long, and the implementation must determine how long + // the packet is. If the packet is in a file, this means that + // the packet extends until the end of the file. In general, + // an implementation SHOULD NOT use indeterminate-length + // packets except where the end of the data will be clear + // from the context, and even then it is better to use a + // definite length, or a new format header. The new format + // headers described below have a mechanism for precisely + // encoding data of indeterminate length. + } + + } else // 4.2.2. New Format Packet Lengths + { + + // 4.2.2.1. One-Octet Lengths + if (input[mypos].charCodeAt() < 192) { + packet_length = input[mypos++].charCodeAt(); + util.print_debug("1 byte length:" + packet_length); + // 4.2.2.2. Two-Octet Lengths + } else if (input[mypos].charCodeAt() >= 192 + && input[mypos].charCodeAt() < 224) { + packet_length = ((input[mypos++].charCodeAt() - 192) << 8) + + (input[mypos++].charCodeAt()) + 192; + util.print_debug("2 byte length:" + packet_length); + // 4.2.2.4. Partial Body Lengths + } else if (input[mypos].charCodeAt() > 223 + && input[mypos].charCodeAt() < 255) { + packet_length = 1 << (input[mypos++].charCodeAt() & 0x1F); + util.print_debug("4 byte length:" + packet_length); + // EEEK, we're reading the full data here... + var mypos2 = mypos + packet_length; + bodydata = input.substring(mypos, mypos + packet_length); + while (true) { + if (input[mypos2].charCodeAt() < 192) { + var tmplen = input[mypos2++].charCodeAt(); + packet_length += tmplen; + bodydata += input.substring(mypos2, mypos2 + tmplen); + mypos2 += tmplen; + break; + } else if (input[mypos2].charCodeAt() >= 192 + && input[mypos2].charCodeAt() < 224) { + var tmplen = ((input[mypos2++].charCodeAt() - 192) << 8) + + (input[mypos2++].charCodeAt()) + 192; + packet_length += tmplen; + bodydata += input.substring(mypos2, mypos2 + tmplen); + mypos2 += tmplen; + break; + } else if (input[mypos2].charCodeAt() > 223 + && input[mypos2].charCodeAt() < 255) { + var tmplen = 1 << (input[mypos2++].charCodeAt() & 0x1F); + packet_length += tmplen; + bodydata += input.substring(mypos2, mypos2 + tmplen); + mypos2 += tmplen; + } else { + mypos2++; + var tmplen = (input[mypos2++].charCodeAt() << 24) + | (input[mypos2++].charCodeAt() << 16) + | (input[mypos2++].charCodeAt() << 8) + | input[mypos2++].charCodeAt(); + bodydata += input.substring(mypos2, mypos2 + tmplen); + packet_length += tmplen; + mypos2 += tmplen; + break; + } + } + real_packet_length = mypos2; + // 4.2.2.3. Five-Octet Lengths + } else { + mypos++; + packet_length = (input[mypos++].charCodeAt() << 24) + | (input[mypos++].charCodeAt() << 16) + | (input[mypos++].charCodeAt() << 8) + | input[mypos++].charCodeAt(); + } + } + + // if there was'nt a partial body length: use the specified + // packet_length + if (real_packet_length == -1) { + real_packet_length = packet_length; + } + + if (bodydata == null) { + bodydata = input.substring(mypos, mypos + real_packet_length); + } + + // alert('tag type: '+this.tag+' length: '+packet_length); + var version = 1; // (old format; 2= new format) + // if (input[mypos++].charCodeAt() > 15) + // version = 2; + + switch (tag) { + case 0: // Reserved - a packet tag MUST NOT have this value + break; + case 1: // Public-Key Encrypted Session Key Packet + var result = new openpgp_packet_encryptedsessionkey(); + if (result.read_pub_key_packet(bodydata, 0, packet_length) != null) { + result.headerLength = mypos - position; + result.packetLength = real_packet_length; + return result; + } + break; + case 2: // Signature Packet + var result = new openpgp_packet_signature(); + if (result.read_packet(bodydata, 0, packet_length) != null) { + result.headerLength = mypos - position; + result.packetLength = real_packet_length; + return result; + } + break; + case 3: // Symmetric-Key Encrypted Session Key Packet + var result = new openpgp_packet_encryptedsessionkey(); + if (result.read_symmetric_key_packet(bodydata, 0, packet_length) != null) { + result.headerLength = mypos - position; + result.packetLength = real_packet_length; + return result; + } + break; + case 4: // One-Pass Signature Packet + var result = new openpgp_packet_onepasssignature(); + if (result.read_packet(bodydata, 0, packet_length)) { + result.headerLength = mypos - position; + result.packetLength = real_packet_length; + return result; + } + break; + case 5: // Secret-Key Packet + var result = new openpgp_packet_keymaterial(); + result.header = input.substring(position, mypos); + if (result.read_tag5(bodydata, 0, packet_length) != null) { + result.headerLength = mypos - position; + result.packetLength = real_packet_length; + return result; + } + break; + case 6: // Public-Key Packet + var result = new openpgp_packet_keymaterial(); + result.header = input.substring(position, mypos); + if (result.read_tag6(bodydata, 0, packet_length) != null) { + result.headerLength = mypos - position; + result.packetLength = real_packet_length; + return result; + } + break; + case 7: // Secret-Subkey Packet + var result = new openpgp_packet_keymaterial(); + if (result.read_tag7(bodydata, 0, packet_length) != null) { + result.headerLength = mypos - position; + result.packetLength = real_packet_length; + return result; + } + break; + case 8: // Compressed Data Packet + var result = new openpgp_packet_compressed(); + if (result.read_packet(bodydata, 0, packet_length) != null) { + result.headerLength = mypos - position; + result.packetLength = real_packet_length; + return result; + } + break; + case 9: // Symmetrically Encrypted Data Packet + var result = new openpgp_packet_encrypteddata(); + if (result.read_packet(bodydata, 0, packet_length) != null) { + result.headerLength = mypos - position; + result.packetLength = real_packet_length; + return result; + } + break; + case 10: // Marker Packet = PGP (0x50, 0x47, 0x50) + var result = new openpgp_packet_marker(); + if (result.read_packet(bodydata, 0, packet_length) != null) { + result.headerLength = mypos - position; + result.packetLength = real_packet_length; + return result; + } + break; + case 11: // Literal Data Packet + var result = new openpgp_packet_literaldata(); + if (result.read_packet(bodydata, 0, packet_length) != null) { + result.headerLength = mypos - position; + result.header = input.substring(position, mypos); + result.packetLength = real_packet_length; + return result; + } + break; + case 12: // Trust Packet + // TODO: to be implemented + break; + case 13: // User ID Packet + var result = new openpgp_packet_userid(); + if (result.read_packet(bodydata, 0, packet_length) != null) { + result.headerLength = mypos - position; + result.packetLength = real_packet_length; + return result; + } + break; + case 14: // Public-Subkey Packet + var result = new openpgp_packet_keymaterial(); + result.header = input.substring(position, mypos); + if (result.read_tag14(bodydata, 0, packet_length) != null) { + result.headerLength = mypos - position; + result.packetLength = real_packet_length; + return result; + } + break; + case 17: // User Attribute Packet + var result = new openpgp_packet_userattribute(); + if (result.read_packet(bodydata, 0, packet_length) != null) { + result.headerLength = mypos - position; + result.packetLength = real_packet_length; + return result; + } + break; + case 18: // Sym. Encrypted and Integrity Protected Data Packet + var result = new openpgp_packet_encryptedintegrityprotecteddata(); + if (result.read_packet(bodydata, 0, packet_length) != null) { + result.headerLength = mypos - position; + result.packetLength = real_packet_length; + return result; + } + break; + case 19: // Modification Detection Code Packet + var result = new openpgp_packet_modificationdetectioncode(); + if (result.read_packet(bodydata, 0, packet_length) != null) { + result.headerLength = mypos - position; + result.packetLength = real_packet_length; + return result; + } + break; + default: + util.print_error("openpgp.packet.js\n" + + "[ERROR] openpgp_packet: failed to parse packet @:" + + mypos + "\nchar:'" + + util.hexstrdump(input.substring(mypos)) + "'\ninput:" + + util.hexstrdump(input)); + return null; + break; + } + } + + this.read_packet = read_packet; +} + +var openpgp_packet = new _openpgp_packet(); diff --git a/src/packet/openpgp.packet.keymaterial.js b/src/packet/openpgp.packet.keymaterial.js new file mode 100755 index 00000000..596d9740 --- /dev/null +++ b/src/packet/openpgp.packet.keymaterial.js @@ -0,0 +1,689 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * Implementation of the Key Material Packet (Tag 5,6,7,14) + * + * RFC4480 5.5: + * A key material packet contains all the information about a public or + * private key. There are four variants of this packet type, and two + * major versions. Consequently, this section is complex. + */ +function openpgp_packet_keymaterial() { + // members: + this.publicKeyAlgorithm = null; + this.tagType = null; + this.creationTime = null; + this.version = null; + this.expiration = null;// V3 + this.MPIs = null; + this.secMPIs = null; + this.publicKey = null; + this.symmetricEncryptionAlgorithm = null; + this.s2kUsageConventions = null; + this.IVLength = null; + this.encryptedMPIData = null; + this.hasUnencryptedSecretKeyData = null; + this.checksum = null; + this.parentNode = null; + this.subKeySignature = null; + this.subKeyRevocationSignature = null; + + // 5.5.1. Key Packet Variants + + // 5.5.1.3. Secret-Key Packet (Tag 5) + /** + * This function reads the payload of a secret key packet (Tag 5) + * and initializes the openpgp_packet_keymaterial + * @param input input string to read the packet from + * @param position start position for the parser + * @param len length of the packet or remaining length of input + * @return openpgp_packet_keymaterial object + */ + function read_tag5(input, position, len) { + this.tagType = 5; + this.read_priv_key(input, position, len); + return this; + } + + // 5.5.1.1. Public-Key Packet (Tag 6) + /** + * This function reads the payload of a public key packet (Tag 6) + * and initializes the openpgp_packet_keymaterial + * @param input input string to read the packet from + * @param position start position for the parser + * @param len length of the packet or remaining length of input + * @return openpgp_packet_keymaterial object + */ + function read_tag6(input, position, len) { + // A Public-Key packet starts a series of packets that forms an OpenPGP + // key (sometimes called an OpenPGP certificate). + this.tagType = 6; + this.packetLength = len; + this.read_pub_key(input, position,len); + + return this; + } + + // 5.5.1.4. Secret-Subkey Packet (Tag 7) + /** + * This function reads the payload of a secret key sub packet (Tag 7) + * and initializes the openpgp_packet_keymaterial + * @param input input string to read the packet from + * @param position start position for the parser + * @param len length of the packet or remaining length of input + * @return openpgp_packet_keymaterial object + */ + function read_tag7(input, position, len) { + this.tagType = 7; + this.packetLength = len; + return this.read_priv_key(input, position, len); + } + + // 5.5.1.2. Public-Subkey Packet (Tag 14) + /** + * This function reads the payload of a public key sub packet (Tag 14) + * and initializes the openpgp_packet_keymaterial + * @param input input string to read the packet from + * @param position start position for the parser + * @param len length of the packet or remaining length of input + * @return openpgp_packet_keymaterial object + */ + function read_tag14(input, position, len) { + this.subKeySignature = null; + this.subKeyRevocationSignature = new Array(); + this.tagType = 14; + this.packetLength = len; + this.read_pub_key(input, position,len); + return this; + } + + /** + * Internal Parser for public keys as specified in RFC 4880 section 5.5.2 Public-Key Packet Formats + * called by read_tag<num> + * @param input input string to read the packet from + * @param position start position for the parser + * @param len length of the packet or remaining length of input + * @return this object with attributes set by the parser + */ + function read_pub_key(input, position, len) { + var mypos = position; + // A one-octet version number (3 or 4). + this.version = input[mypos++].charCodeAt(); + if (this.version == 3) { + // A four-octet number denoting the time that the key was created. + this.creationTime = new Date(((input[mypos++].charCodeAt() << 24) | + (input[mypos++].charCodeAt() << 16) | + (input[mypos++].charCodeAt() << 8) | + (input[mypos++].charCodeAt()))*1000); + + // - A two-octet number denoting the time in days that this key is + // valid. If this number is zero, then it does not expire. + this.expiration = (input[mypos++].charCodeAt() << 8) & input[mypos++].charCodeAt(); + + // - A one-octet number denoting the public-key algorithm of this key. + this.publicKeyAlgorithm = input[mypos++].charCodeAt(); + var mpicount = 0; + // - A series of multiprecision integers comprising the key material: + // Algorithm-Specific Fields for RSA public keys: + // - a multiprecision integer (MPI) of RSA public modulus n; + // - an MPI of RSA public encryption exponent e. + if (this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4) + mpicount = 2; + // Algorithm-Specific Fields for Elgamal public keys: + // - MPI of Elgamal prime p; + // - MPI of Elgamal group generator g; + // - MPI of Elgamal public key value y (= g**x mod p where x is secret). + + else if (this.publicKeyAlgorithm == 16) + mpicount = 3; + // Algorithm-Specific Fields for DSA public keys: + // - MPI of DSA prime p; + // - MPI of DSA group order q (q is a prime divisor of p-1); + // - MPI of DSA group generator g; + // - MPI of DSA public-key value y (= g**x mod p where x is secret). + else if (this.publicKeyAlgorithm == 17) + mpicount = 4; + + this.MPIs = new Array(); + for (var i = 0; i < mpicount; i++) { + this.MPIs[i] = new openpgp_type_mpi(); + if (this.MPIs[i].read(input, mypos, (mypos-position)) != null && + !this.packetLength < (mypos-position)) { + mypos += this.MPIs[i].packetLength; + } else { + util.print_error("openpgp.packet.keymaterial.js\n"+'error reading MPI @:'+mypos); + } + } + this.packetLength = mypos-position; + } else if (this.version == 4) { + // - A four-octet number denoting the time that the key was created. + this.creationTime = new Date(((input[mypos++].charCodeAt() << 24) | + (input[mypos++].charCodeAt() << 16) | + (input[mypos++].charCodeAt() << 8) | + (input[mypos++].charCodeAt()))*1000); + + // - A one-octet number denoting the public-key algorithm of this key. + this.publicKeyAlgorithm = input[mypos++].charCodeAt(); + var mpicount = 0; + // - A series of multiprecision integers comprising the key material: + // Algorithm-Specific Fields for RSA public keys: + // - a multiprecision integer (MPI) of RSA public modulus n; + // - an MPI of RSA public encryption exponent e. + if (this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4) + mpicount = 2; + // Algorithm-Specific Fields for Elgamal public keys: + // - MPI of Elgamal prime p; + // - MPI of Elgamal group generator g; + // - MPI of Elgamal public key value y (= g**x mod p where x is secret). + else if (this.publicKeyAlgorithm == 16) + mpicount = 3; + + // Algorithm-Specific Fields for DSA public keys: + // - MPI of DSA prime p; + // - MPI of DSA group order q (q is a prime divisor of p-1); + // - MPI of DSA group generator g; + // - MPI of DSA public-key value y (= g**x mod p where x is secret). + else if (this.publicKeyAlgorithm == 17) + mpicount = 4; + + this.MPIs = new Array(); + var i = 0; + for (var i = 0; i < mpicount; i++) { + this.MPIs[i] = new openpgp_type_mpi(); + if (this.MPIs[i].read(input, mypos, (mypos-position)) != null && + !this.packetLength < (mypos-position)) { + mypos += this.MPIs[i].packetLength; + } else { + util.print_error("openpgp.packet.keymaterial.js\n"+'error reading MPI @:'+mypos); + } + } + this.packetLength = mypos-position; + } else { + return null; + } + this.data = input.substring(position, mypos); + this.packetdata = input.substring(position, mypos); + return this; + } + + // 5.5.3. Secret-Key Packet Formats + + /** + * Internal parser for private keys as specified in RFC 4880 section 5.5.3 + * @param input input string to read the packet from + * @param position start position for the parser + * @param len length of the packet or remaining length of input + * @return this object with attributes set by the parser + */ + function read_priv_key(input,position, len) { + // - A Public-Key or Public-Subkey packet, as described above. + this.publicKey = new openpgp_packet_keymaterial(); + if (this.publicKey.read_pub_key(input,position, len) == null) { + util.print_error("openpgp.packet.keymaterial.js\n"+"Failed reading public key portion of a private key: "+input[position].charCodeAt()+" "+position+" "+len+"\n Aborting here..."); + return null; + } + this.publicKey.header = openpgp_packet.write_old_packet_header(6,this.publicKey.packetLength); + // this.publicKey.header = String.fromCharCode(0x99) + String.fromCharCode(this.publicKey.packetLength >> 8 & 0xFF)+String.fromCharCode(this.publicKey.packetLength & 0xFF); + var mypos = position + this.publicKey.data.length; + this.packetLength = len; + + // - One octet indicating string-to-key usage conventions. Zero + // indicates that the secret-key data is not encrypted. 255 or 254 + // indicates that a string-to-key specifier is being given. Any + // other value is a symmetric-key encryption algorithm identifier. + this.s2kUsageConventions = input[mypos++].charCodeAt(); + + if (this.s2kUsageConventions == 0) + this.hasUnencryptedSecretKeyData = true; + + // - [Optional] If string-to-key usage octet was 255 or 254, a one- + // octet symmetric encryption algorithm. + if (this.s2kUsageConventions == 255 || this.s2kUsageConventions == 254) { + this.symmetricEncryptionAlgorithm = input[mypos++].charCodeAt(); + } + + // - [Optional] If string-to-key usage octet was 255 or 254, a + // string-to-key specifier. The length of the string-to-key + // specifier is implied by its type, as described above. + if (this.s2kUsageConventions == 255 || this.s2kUsageConventions == 254) { + this.s2k = new openpgp_type_s2k(); + this.s2k.read(input, mypos); + mypos +=this.s2k.s2kLength; + } + + // - [Optional] If secret data is encrypted (string-to-key usage octet + // not zero), an Initial Vector (IV) of the same length as the + // cipher's block size. + this.symkeylength = 0; + if (this.s2kUsageConventions != 0 && this.s2kUsageConventions != 255 && + this.s2kUsageConventions != 254) { + this.symmetricEncryptionAlgorithm = this.s2kUsageConventions; + } + if (this.s2kUsageConventions != 0) { + this.hasIV = true; + switch (this.symmetricEncryptionAlgorithm) { + case 1: // - IDEA [IDEA] + util.print_error("openpgp.packet.keymaterial.js\n"+"symmetric encrytryption algorithim: IDEA is not implemented"); + return null; + case 2: // - TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192) + case 3: // - CAST5 (128 bit key, as per [RFC2144]) + this.IVLength = 8; + break; + case 4: // - Blowfish (128 bit key, 16 rounds) [BLOWFISH] + case 7: // - AES with 128-bit key [AES] + case 8: // - AES with 192-bit key + case 9: // - AES with 256-bit key + this.IVLength = 16; + break; + case 10: // - Twofish with 256-bit key [TWOFISH] + this.IVLength = 32; + break; + case 5: // - Reserved + case 6: // - Reserved + default: + util.print_error("openpgp.packet.keymaterial.js\n"+"unknown encryption algorithm for secret key :"+this.symmetricEncryptionAlgorithm); + return null; + } + mypos++; + this.IV = input.substring(mypos, mypos+this.IVLength); + mypos += this.IVLength; + } + // - Plain or encrypted multiprecision integers comprising the secret + // key data. These algorithm-specific fields are as described + // below. + + // + // + if (!this.hasUnencryptedSecretKeyData) { + this.encryptedMPIData = input.substring(mypos, len); + mypos += this.encryptedMPIData.length; + } else { + if (this.publicKey.publicKeyAlgorithm > 0 && this.publicKey.publicKeyAlgorithm < 4) { + // Algorithm-Specific Fields for RSA secret keys: + // - multiprecision integer (MPI) of RSA secret exponent d. + // - MPI of RSA secret prime value p. + // - MPI of RSA secret prime value q (p < q). + // - MPI of u, the multiplicative inverse of p, mod q. + this.secMPIs = new Array(); + this.secMPIs[0] = new openpgp_type_mpi(); + this.secMPIs[0].read(input, mypos, len-2- (mypos - position)); + mypos += this.secMPIs[0].packetLength; + this.secMPIs[1] = new openpgp_type_mpi(); + this.secMPIs[1].read(input, mypos, len-2- (mypos - position)); + mypos += this.secMPIs[1].packetLength; + this.secMPIs[2] = new openpgp_type_mpi(); + this.secMPIs[2].read(input, mypos, len-2- (mypos - position)); + mypos += this.secMPIs[2].packetLength; + this.secMPIs[3] = new openpgp_type_mpi(); + this.secMPIs[3].read(input, mypos, len-2- (mypos - position)); + mypos += this.secMPIs[3].packetLength; + } else if (this.publicKey.publicKeyAlgorithm == 16) { + // Algorithm-Specific Fields for Elgamal secret keys: + // - MPI of Elgamal secret exponent x. + this.secMPIs = new Array(); + this.secMPIs[0] = new openpgp_type_mpi(); + this.secMPIs[0].read(input, mypos, len-2- (mypos - position)); + mypos += this.secMPIs[0].packetLength; + } else if (this.publicKey.publicKeyAlgorithm == 17) { + // Algorithm-Specific Fields for DSA secret keys: + // - MPI of DSA secret exponent x. + this.secMPIs = new Array(); + this.secMPIs[0] = new openpgp_type_mpi(); + this.secMPIs[0].read(input, mypos, len-2- (mypos - position)); + mypos += this.secMPIs[0].packetLength; + } + // checksum because s2k usage convention is 0 + this.checksum = new Array(); + this.checksum[0] = input[mypos++].charCodeAt(); + this.checksum[1] = input[mypos++].charCodeAt(); + } + return this; + } + + + /** + * Decrypts the private key MPIs which are needed to use the key. + * openpgp_packet_keymaterial.hasUnencryptedSecretKeyData should be false otherwise + * a call to this function is not needed + * + * @param str_passphrase the passphrase for this private key as string + * @return true if the passphrase was correct; false if not + */ + function decryptSecretMPIs(str_passphrase) { + if (this.hasUnencryptedSecretKeyData) + return this.secMPIs; + // creating a key out of the passphrase + var key = this.s2k.produce_key(str_passphrase); + var cleartextMPIs = ""; + switch (this.symmetricEncryptionAlgorithm) { + case 1: // - IDEA [IDEA] + util.print_error("openpgp.packet.keymaterial.js\n"+"symmetric encryption algorithim: IDEA is not implemented"); + return false; + case 2: // - TripleDES (DES-EDE, [SCHNEIER] [HAC] - 168 bit key derived from 192) + cleartextMPIs = normal_cfb_decrypt(function(block, key) { + return des(key, block,1,null,0); + }, this.IVLength, key, this.encryptedMPIData, this.IV); + break; + case 3: // - CAST5 (128 bit key, as per [RFC2144]) + cleartextMPIs = normal_cfb_decrypt(function(block, key) { + var cast5 = new openpgp_symenc_cast5(); + cast5.setKey(key); + return cast5.encrypt(util.str2bin(block)); + }, this.IVLength, util.str2bin(key.substring(0,16)), this.encryptedMPIData, this.IV); + break; + case 4: // - Blowfish (128 bit key, 16 rounds) [BLOWFISH] + cleartextMPIs = normal_cfb_decrypt(function(block, key) { + var blowfish = new Blowfish(key); + return blowfish.encrypt(block); + }, this.IVLength, key, this.encryptedMPIData, this.IV); + break; + case 7: // - AES with 128-bit key [AES] + case 8: // - AES with 192-bit key + case 9: // - AES with 256-bit key + cleartextMPIs = normal_cfb_decrypt(AESencrypt, + this.IVLength, keyExpansion(key), this.encryptedMPIData, this.IV); + break; + case 10: // - Twofish with 256-bit key [TWOFISH] + util.print_error("openpgp.packet.keymaterial.js\n"+"Key material is encrypted with twofish: not implemented"); + return false;; + case 5: // - Reserved + case 6: // - Reserved + default: + util.print_error("openpgp.packet.keymaterial.js\n"+"unknown encryption algorithm for secret key :"+this.symmetricEncryptionAlgorithm); + return false; + } + + if (cleartextMPIs == null) { + util.print_error("openpgp.packet.keymaterial.js\n"+"cleartextMPIs was null"); + return false; + } + + var cleartextMPIslength = cleartextMPIs.length; + if (this.s2kUsageConventions == 254 && + str_sha1(cleartextMPIs.substring(0,cleartextMPIs.length - 20)) == + cleartextMPIs.substring(cleartextMPIs.length - 20)) { + cleartextMPIslength -= 20; + } else if (this.s2kUsageConcentions != 254 && util.calc_checksum(cleartextMPIs.substring(0,cleartextMPIs.length - 2)) == + (cleartextMPIs.charCodeAt(cleartextMPIs.length -2) << 8 | cleartextMPIs.charCodeAt(cleartextMPIs.length -1))) { + cleartextMPIslength -= 2; + } else { + return false; + } + + if (this.publicKey.publicKeyAlgorithm > 0 && this.publicKey.publicKeyAlgorithm < 4) { + // Algorithm-Specific Fields for RSA secret keys: + // - multiprecision integer (MPI) of RSA secret exponent d. + // - MPI of RSA secret prime value p. + // - MPI of RSA secret prime value q (p < q). + // - MPI of u, the multiplicative inverse of p, mod q. + var mypos = 0; + this.secMPIs = new Array(); + this.secMPIs[0] = new openpgp_type_mpi(); + this.secMPIs[0].read(cleartextMPIs, 0, cleartextMPIslength); + mypos += this.secMPIs[0].packetLength; + this.secMPIs[1] = new openpgp_type_mpi(); + this.secMPIs[1].read(cleartextMPIs, mypos, cleartextMPIslength-mypos); + mypos += this.secMPIs[1].packetLength; + this.secMPIs[2] = new openpgp_type_mpi(); + this.secMPIs[2].read(cleartextMPIs, mypos, cleartextMPIslength-mypos); + mypos += this.secMPIs[2].packetLength; + this.secMPIs[3] = new openpgp_type_mpi(); + this.secMPIs[3].read(cleartextMPIs, mypos, cleartextMPIslength-mypos); + mypos += this.secMPIs[3].packetLength; + } else if (this.publicKey.publicKeyAlgorithm == 16) { + // Algorithm-Specific Fields for Elgamal secret keys: + // - MPI of Elgamal secret exponent x. + this.secMPIs = new Array(); + this.secMPIs[0] = new openpgp_type_mpi(); + this.secMPIs[0].read(cleartextMPIs, 0, cleartextMPIs); + } else if (this.publicKey.publicKeyAlgorithm == 17) { + // Algorithm-Specific Fields for DSA secret keys: + // - MPI of DSA secret exponent x. + this.secMPIs = new Array(); + this.secMPIs[0] = new openpgp_type_mpi(); + this.secMPIs[0].read(cleartextMPIs, 0, cleartextMPIslength); + } + return true; + } + + /** + * Generates Debug output + * @return String which gives some information about the keymaterial + */ + function toString() { + var result = ""; + switch (this.tagType) { + case 6: + result += '5.5.1.1. Public-Key Packet (Tag 6)\n'+ + ' length: '+this.packetLength+'\n'+ + ' version: '+this.version+'\n'+ + ' creation time: '+this.creationTime+'\n'+ + ' expiration time: '+this.expiration+'\n'+ + ' publicKeyAlgorithm: '+this.publicKeyAlgorithm+'\n'; + break; + case 14: + result += '5.5.1.2. Public-Subkey Packet (Tag 14)\n'+ + ' length: '+this.packetLength+'\n'+ + ' version: '+this.version+'\n'+ + ' creation time: '+this.creationTime+'\n'+ + ' expiration time: '+this.expiration+'\n'+ + ' publicKeyAlgorithm: '+this.publicKeyAlgorithm+'\n'; + break; + case 5: + result +='5.5.1.3. Secret-Key Packet (Tag 5)\n'+ + ' length: '+this.packetLength+'\n'+ + ' version: '+this.publicKey.version+'\n'+ + ' creation time: '+this.publicKey.creationTime+'\n'+ + ' expiration time: '+this.publicKey.expiration+'\n'+ + ' publicKeyAlgorithm: '+this.publicKey.publicKeyAlgorithm+'\n'; + break; + case 7: + result += '5.5.1.4. Secret-Subkey Packet (Tag 7)\n'+ + ' length: '+this.packetLength+'\n'+ + ' version[1]: '+(this.version == 4)+'\n'+ + ' creationtime[4]: '+this.creationTime+'\n'+ + ' expiration[2]: '+this.expiration+'\n'+ + ' publicKeyAlgorithm: '+this.publicKeyAlgorithm+'\n'; + break; + default: + result += 'unknown key material packet\n'; + } + if (this.MPIs != null) { + result += "Public Key MPIs:\n"; + for (var i = 0; i < this.MPIs.length; i++) { + result += this.MPIs[i].toString(); + } + } + if (this.publicKey != null && this.publicKey.MPIs != null) { + result += "Public Key MPIs:\n"; + for (var i = 0; i < this.publicKey.MPIs.length; i++) { + result += this.publicKey.MPIs[i].toString(); + } + } + if (this.secMPIs != null) { + result += "Secret Key MPIs:\n"; + for (var i = 0; i < this.secMPIs.length; i++) { + result += this.secMPIs[i].toString(); + } + } + + if (this.subKeySignature != null) + result += "subKey Signature:\n"+this.subKeySignature.toString(); + + if (this.subKeyRevocationSignature != null ) + result += "subKey Revocation Signature:\n"+this.subKeyRevocationSignature.toString(); + return result; + } + + /** + * Continue parsing packets belonging to the key material such as signatures + * @param parent_node [openpgp_*] the parent object + * @param input [String] input string to read the packet(s) from + * @param position [integer] start position for the parser + * @param len [integer] length of the packet(s) or remaining length of input + * @return [integer] length of nodes read + */ + function read_nodes(parent_node, input, position, len) { + this.parentNode = parent_node; + if (this.tagType == 14) { // public sub-key packet + var pos = position; + var result = null; + while (input.length != pos) { + var l = input.length - pos; + result = openpgp_packet.read_packet(input, pos, l); + if (result == null) { + util.print_error("openpgp.packet.keymaterial.js\n"+'[user_keymat_pub]parsing ends here @:' + pos + " l:" + l); + break; + } else { + + switch (result.tagType) { + case 2: // Signature Packet certification signature + if (result.signatureType == 24) { // subkey binding signature + this.subKeySignature = result; + pos += result.packetLength + result.headerLength; + break; + } else if (result.signatureType == 40) { // subkey revocation signature + this.subKeyRevocationSignature = result; + pos += result.packetLength + result.headerLength; + break; + } else { + util.print_error("openpgp.packet.keymaterial.js\nunknown signature:"+result.toString()); + } + + default: + this.data = input; + this.position = position - this.parentNode.packetLength; + this.len = pos - position; + return this.len; + break; + } + } + } + this.data = input; + this.position = position - this.parentNode.packetLength; + this.len = pos - position; + return this.len; + } else if (this.tagType == 7) { // private sub-key packet + var pos = position; + while (input.length != pos) { + var result = openpgp_packet.read_packet(input, pos, len - (pos - position)); + if (result == null) { + util.print_error("openpgp.packet.keymaterial.js\n"+'[user_keymat_priv] parsing ends here @:' + pos); + break; + } else { + switch (result.tagType) { + case 2: // Signature Packet certification signature + if (result.signatureType == 24) // subkey embedded signature + this.subKeySignature = result; + else if (result.signatureType == 40) // subkey revocation signature + this.subKeyRevocationSignature[this.subKeyRevocationSignature] = result; + pos += result.packetLength + result.headerLength; + break; + default: + this.data = input; + this.position = position - this.parentNode.packetLength; + this.len = pos - position; + return this.len; + } + } + } + this.data = input; + this.position = position - this.parentNode.packetLength; + this.len = pos - position; + return this.len; + } else { + util.print_error("openpgp.packet.keymaterial.js\n"+"unknown parent node for a key material packet "+parent_node.tagType); + } + } + + /** + * Checks the validity for usage of this (sub)key + * @return 0 = bad key, 1 = expired, 2 = revoked, 3 = valid + */ + function verifyKey() { + if (this.tagType == 14) { + if (this.subKeySignature == null) + return 0; + if (this.subKeySignature.version == 4 && + this.subKeySignature.keyNeverExpires != null && + !this.subKeySignature.keyNeverExpires && + new Date((this.subKeySignature.keyExpirationTime*1000)+ this.creationTime.getTime()) < new Date()) + return 1; + var hashdata = String.fromCharCode(0x99)+this.parentNode.header.substring(1)+this.parentNode.data+ + String.fromCharCode(0x99)+this.header.substring(1)+this.packetdata; + if (!this.subKeySignature.verify(hashdata,this.parentNode)) { + return 0; + } + for (var i = 0; i < this.subKeyRevocationSignature.length; i++) { + if (this.subKeyRevocationSignature[i]) + var hashdata = String.fromCharCode(0x99)+this.parentNode.header.substring(1)+this.parentNode.data+ + String.fromCharCode(0x99)+this.header.substring(1)+this.packetdata; + if (this.subKeyRevocationSignature[i].verify(hashdata, this.parentNode)) + return 2; + else + return 0; + } + } + return 3; + } + + /** + * calculates the key id of they key + * @return [String] a 8 byte key id + */ + function getKeyId() { + if (this.version == 4) { + var f = this.getFingerprint(); + return f.substring(12,20); + } else if (this.version == 3 && this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4) { + var key_id = this.MPIs[0].substring((this.MPIs[0].mpiByteLength-8)); + util.print_debug("openpgp.msg.publickey read_nodes:\n"+"V3 key ID: "+key_id); + return key_id; + } + } + + /** + * calculates the fingerprint of the key + * @return [String] a string containing the fingerprint + */ + function getFingerprint() { + if (this.version == 4) { + var tohash= this.header+this.data; + //if (this.tagType == 14) { + tohash = String.fromCharCode(0x99)+this.header.substring(1)+this.packetdata; + util.print_debug("openpgp.msg.publickey creating subkey fingerprint by hashing:"+util.hexstrdump(tohash)+"\npublickeyalgorithm: "+this.publicKeyAlgorithm); + //} + return str_sha1(tohash, tohash.length); + } else if (this.version == 3 && this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4) { + return MD5(this.MPIs[0].MPI); + } + } + + this.read_tag5 = read_tag5; + this.read_tag6 = read_tag6; + this.read_tag7 = read_tag7; + this.read_tag14 = read_tag14; + this.toString = toString; + this.read_pub_key = read_pub_key; + this.read_priv_key = read_priv_key; + this.decryptSecretMPIs = decryptSecretMPIs; + this.read_nodes = read_nodes; + this.verifyKey = verifyKey; + this.getKeyId = getKeyId; + this.getFingerprint = getFingerprint; +} diff --git a/src/packet/openpgp.packet.literaldata.js b/src/packet/openpgp.packet.literaldata.js new file mode 100755 index 00000000..ea80ad71 --- /dev/null +++ b/src/packet/openpgp.packet.literaldata.js @@ -0,0 +1,99 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * Implementation of the Literal Data Packet (Tag 11) + * + * RFC4880 5.9: A Literal Data packet contains the body of a message; data that + * is not to be further interpreted. + */ +function openpgp_packet_literaldata() { + this.tagType = 11; + + /** + * parsing function for a literal data packet (tag 11). + * + * @param input + * [string] payload of a tag 11 packet + * @param position + * [integer] position to start reading from the input string + * @param len + * [integer] length of the packet or the remaining length of + * input at position + * @return [openpgp_packet_encrypteddata] object representation + */ + function read_packet(input, position, len) { + this.packetLength = len; + // - A one-octet field that describes how the data is formatted. + + this.format = input[position]; + this.filename = input.substr(position + 2, input + .charCodeAt(position + 1)); + this.date = new Date(parseInt(input.substr(position + 2 + + input.charCodeAt(position + 1), 4)) * 1000); + this.data = input.substring(position + 6 + + input.charCodeAt(position + 1)); + return this; + } + + /** + * Creates a string representation of the packet + * + * @param data + * [String] the data to be inserted as body + * @return [String] string-representation of the packet + */ + function write_packet(data) { + data = data.replace(/\r\n/g, "\n").replace(/\n/g, "\r\n"); + this.filename = "msg.txt"; + this.date = new Date(); + this.format = 't'; + var result = openpgp_packet.write_packet_header(11, data.length + 6 + + this.filename.length); + result += this.format; + result += String.fromCharCode(this.filename.length); + result += this.filename; + result += String + .fromCharCode((Math.round(this.date.getTime() / 1000) >> 24) & 0xFF); + result += String + .fromCharCode((Math.round(this.date.getTime() / 1000) >> 16) & 0xFF); + result += String + .fromCharCode((Math.round(this.date.getTime() / 1000) >> 8) & 0xFF); + result += String + .fromCharCode(Math.round(this.date.getTime() / 1000) & 0xFF); + result += data; + this.data = data; + return result; + } + + /** + * generates debug output (pretty print) + * + * @return String which gives some information about the keymaterial + */ + function toString() { + return '5.9. Literal Data Packet (Tag 11)\n' + ' length: ' + + this.packetLength + '\n' + ' format: ' + this.format + + '\n' + ' filename:' + this.filename + '\n' + + ' date: ' + this.date + '\n' + ' data: |' + + this.data + '|\n' + ' rdata: |' + this.real_data + '|\n'; + } + + this.read_packet = read_packet; + this.toString = toString; + this.write_packet = write_packet; +} diff --git a/src/packet/openpgp.packet.marker.js b/src/packet/openpgp.packet.marker.js new file mode 100755 index 00000000..d823e10a --- /dev/null +++ b/src/packet/openpgp.packet.marker.js @@ -0,0 +1,64 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * Implementation of the strange "Marker packet" (Tag 10) + * + * RFC4880 5.8: An experimental version of PGP used this packet as the Literal + * packet, but no released version of PGP generated Literal packets with this + * tag. With PGP 5.x, this packet has been reassigned and is reserved for use as + * the Marker packet. + * + * Such a packet MUST be ignored when received. + */ +function openpgp_packet_marker() { + this.tagType = 10; + /** + * parsing function for a literal data packet (tag 10). + * + * @param input + * [string] payload of a tag 10 packet + * @param position + * [integer] position to start reading from the input string + * @param len + * [integer] length of the packet or the remaining length of + * input at position + * @return [openpgp_packet_encrypteddata] object representation + */ + function read_packet(input, position, len) { + this.packetLength = 3; + if (input[position].charCodeAt() == 0x50 && // P + input[position + 1].charCodeAt() == 0x47 && // G + input[position + 2].charCodeAt() == 0x50) // P + return this; + // marker packet does not contain "PGP" + return null; + } + + /** + * Generates Debug output + * + * @return String which gives some information about the keymaterial + */ + function toString() { + return "5.8. Marker Packet (Obsolete Literal Packet) (Tag 10)\n" + + " packet reads: \"PGP\"\n"; + } + + this.read_packet = read_packet; + this.toString = toString; +} \ No newline at end of file diff --git a/src/packet/openpgp.packet.modificationdetectioncode.js b/src/packet/openpgp.packet.modificationdetectioncode.js new file mode 100755 index 00000000..673005e3 --- /dev/null +++ b/src/packet/openpgp.packet.modificationdetectioncode.js @@ -0,0 +1,79 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * Implementation of the Modification Detection Code Packet (Tag 19) + * + * RFC4880 5.14: The Modification Detection Code packet contains a SHA-1 hash of + * plaintext data, which is used to detect message modification. It is only used + * with a Symmetrically Encrypted Integrity Protected Data packet. The + * Modification Detection Code packet MUST be the last packet in the plaintext + * data that is encrypted in the Symmetrically Encrypted Integrity Protected + * Data packet, and MUST appear in no other place. + */ + +function openpgp_packet_modificationdetectioncode() { + this.tagType = 19; + this.hash = null; + /** + * parsing function for a modification detection code packet (tag 19). + * + * @param input + * [String] payload of a tag 19 packet + * @param position + * [Integer] position to start reading from the input string + * @param len + * [Integer] length of the packet or the remaining length of + * input at position + * @return [openpgp_packet_encrypteddata] object representation + */ + function read_packet(input, position, len) { + this.packetLength = len; + + if (len != 20) { + util + .print_error("openpgp.packet.modificationdetectioncode.js\n" + + 'invalid length for a modification detection code packet!' + + len); + return null; + } + // - A 20-octet SHA-1 hash of the preceding plaintext data of the + // Symmetrically Encrypted Integrity Protected Data packet, + // including prefix data, the tag octet, and length octet of the + // Modification Detection Code packet. + this.hash = input.substring(position, position + 20); + return this; + } + + /* + * this packet is created within the encryptedintegrityprotected packet + * function write_packet(data) { } + */ + + /** + * generates debug output (pretty print) + * + * @return String which gives some information about the modification + * detection code + */ + function toString() { + return '5.14 Modification detection code packet\n' + ' bytes (' + + this.hash.length + '): [' + util.hexstrdump(this.hash) + ']'; + } + this.read_packet = read_packet; + this.toString = toString; +}; diff --git a/src/packet/openpgp.packet.onepasssignature.js b/src/packet/openpgp.packet.onepasssignature.js new file mode 100755 index 00000000..10ff04bc --- /dev/null +++ b/src/packet/openpgp.packet.onepasssignature.js @@ -0,0 +1,118 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * Implementation of the One-Pass Signature Packets (Tag 4) + * + * RFC4880 5.4: + * The One-Pass Signature packet precedes the signed data and contains + * enough information to allow the receiver to begin calculating any + * hashes needed to verify the signature. It allows the Signature + * packet to be placed at the end of the message, so that the signer + * can compute the entire signed message in one pass. + */ +function openpgp_packet_onepasssignature() { + this.tagType = 4; + this.version = null; // A one-octet version number. The current version is 3. + this.type == null; // A one-octet signature type. Signature types are described in RFC4880 Section 5.2.1. + this.hashAlgorithm = null; // A one-octet number describing the hash algorithm used. (See RFC4880 9.4) + this.publicKeyAlgorithm = null; // A one-octet number describing the public-key algorithm used. (See RFC4880 9.1) + this.signingKeyId = null; // An eight-octet number holding the Key ID of the signing key. + this.flags = null; // A one-octet number holding a flag showing whether the signature is nested. A zero value indicates that the next packet is another One-Pass Signature packet that describes another signature to be applied to the same message data. + + /** + * parsing function for a one-pass signature packet (tag 4). + * @param input [string] payload of a tag 4 packet + * @param position [integer] position to start reading from the input string + * @param len [integer] length of the packet or the remaining length of input at position + * @return [openpgp_packet_encrypteddata] object representation + */ + function read_packet(input, position, len) { + this.packetLength = len; + var mypos = position; + // A one-octet version number. The current version is 3. + this.version = input.charCodeAt(mypos++); + + // A one-octet signature type. Signature types are described in + // Section 5.2.1. + this.type = input.charCodeAt(mypos++); + + // A one-octet number describing the hash algorithm used. + this.hashAlgorithm = input.charCodeAt(mypos++); + + // A one-octet number describing the public-key algorithm used. + this.publicKeyAlgorithm = input.charCodeAt(mypos++); + // An eight-octet number holding the Key ID of the signing key. + this.signingKeyId = new openpgp_type_keyid(); + this.signingKeyId.read_packet(input,mypos); + mypos += 8; + + // A one-octet number holding a flag showing whether the signature + // is nested. A zero value indicates that the next packet is + // another One-Pass Signature packet that describes another + // signature to be applied to the same message data. + this.flags = input.charCodeAt(mypos++); + return this; + } + + /** + * creates a string representation of a one-pass signature packet + * @param type [integer] Signature types as described in RFC4880 Section 5.2.1. + * @param hashalgorithm [integer] the hash algorithm used within the signature + * @param privatekey [openpgp_msg_privatekey] the private key used to generate the signature + * @param length [integer] length of data to be signed + * @param nested [boolean] boolean showing whether the signature is nested. + * "true" indicates that the next packet is another One-Pass Signature packet + * that describes another signature to be applied to the same message data. + * @return [String] a string representation of a one-pass signature packet + */ + function write_packet(type, hashalgorithm, privatekey,length, nested) { + var result =""; + + result += openpgp_packet.write_packet_header(4,13); + result += String.fromCharCode(3); + result += String.fromCharCode(type); + result += String.fromCharCode(hashalgorithm); + result += String.fromCharCode(privatekey.privateKeyPacket.publicKey.publicKeyAlgorithm); + result += privatekey.getKeyId(); + if (nested) + result += String.fromCharCode(0); + else + result += String.fromCharCode(1); + + return result; + } + + /** + * generates debug output (pretty print) + * @return String which gives some information about the one-pass signature packet + */ + function toString() { + return '5.4. One-Pass Signature Packets (Tag 4)\n'+ + ' length: '+this.packetLength+'\n'+ + ' type: '+this.type+'\n'+ + ' keyID: '+this.signingKeyId.toString()+'\n'+ + ' hashA: '+this.hashAlgorithm+'\n'+ + ' pubKeyA:'+this.publicKeyAlgorithm+'\n'+ + ' flags: '+this.flags+'\n'+ + ' version:'+this.version+'\n'; + } + + this.read_packet = read_packet; + this.toString = toString; + this.write_packet = write_packet; +}; \ No newline at end of file diff --git a/src/packet/openpgp.packet.signature.js b/src/packet/openpgp.packet.signature.js new file mode 100755 index 00000000..053bd3e9 --- /dev/null +++ b/src/packet/openpgp.packet.signature.js @@ -0,0 +1,755 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * Implementation of the Signature Packet (Tag 2) + * + * RFC4480 5.2: + * A Signature packet describes a binding between some public key and + * some data. The most common signatures are a signature of a file or a + * block of text, and a signature that is a certification of a User ID. + */ +function openpgp_packet_signature() { + this.tagType = 2; + this.signatureType = null; + this.creationTime = null; + this.keyId = null; + this.signatureData = null; + this.signatureExpirationTime = null; + this.signatureNeverExpires = null; + this.signedHashValue = null; + this.MPIs = null; + this.publicKeyAlgorithm = null; + this.hashAlgorithm = null; + this.exportable = null; + this.trustLevel = null; + this.trustAmount = null; + this.regular_expression = null; + this.revocable = null; + this.keyExpirationTime = null; + this.keyNeverExpires = null; + this.preferredSymmetricAlgorithms = null; + this.revocationKeyClass = null; + this.revocationKeyAlgorithm = null; + this.revocationKeyFingerprint = null; + this.issuerKeyId = null; + this.notationFlags = null; + this.notationName = null; + this.notationValue = null; + this.preferredHashAlgorithms = null; + this.preferredCompressionAlgorithms = null; + this.keyServerPreferences = null; + this.preferredKeyServer = null; + this.isPrimaryUserID = null; + this.policyURI = null; + this.keyFlags = null; + this.signersUserId = null; + this.reasonForRevocationFlag = null; + this.reasonForRevocationString = null; + this.signatureTargetPublicKeyAlgorithm = null; + this.signatureTargetHashAlgorithm = null; + this.signatureTargetHash = null; + this.embeddedSignature = null; + + + /** + * parsing function for a signature packet (tag 2). + * @param input [string] payload of a tag 2 packet + * @param position [integer] position to start reading from the input string + * @param len [integer] length of the packet or the remaining length of input at position + * @return [openpgp_packet_encrypteddata] object representation + */ + function read_packet(input, position, len) { + this.data = input.substring (position, position+len); + if (len < 0) { + util.print_debug("openpgp.packet.signature.js\n"+"openpgp_packet_signature read_packet length < 0 @:"+position); + return null; + } + var mypos = position; + this.packetLength = len; + // alert('starting parsing signature: '+position+' '+this.packetLength); + this.version = input[mypos++].charCodeAt(); + // switch on version (3 and 4) + switch (this.version) { + case 3: + // One-octet length of following hashed material. MUST be 5. + if (input[mypos++].charCodeAt() != 5) + util.print_debug("openpgp.packet.signature.js\n"+'invalid One-octet length of following hashed material. MUST be 5. @:'+(mypos-1)); + var sigpos = mypos; + // One-octet signature type. + this.signatureType = input[mypos++].charCodeAt(); + + // Four-octet creation time. + this.creationTime = new Date(((input[mypos++].charCodeAt()) << 24 | + (input[mypos++].charCodeAt() << 16) | (input[mypos++].charCodeAt() << 8) | + input[mypos++].charCodeAt())* 1000); + + // storing data appended to data which gets verified + this.signatureData = input.substring(position, mypos); + + // Eight-octet Key ID of signer. + this.keyId = input.substring(mypos, mypos +8); + mypos += 8; + + // One-octet public-key algorithm. + this.publicKeyAlgorithm = input[mypos++].charCodeAt(); + + // One-octet hash algorithm. + this.hashAlgorithm = input[mypos++].charCodeAt(); + + // Two-octet field holding left 16 bits of signed hash value. + this.signedHashValue = (input[mypos++].charCodeAt() << 8) | input[mypos++].charCodeAt(); + var mpicount = 0; + // Algorithm-Specific Fields for RSA signatures: + // - multiprecision integer (MPI) of RSA signature value m**d mod n. + if (this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4) + mpicount = 1; + // Algorithm-Specific Fields for DSA signatures: + // - MPI of DSA value r. + // - MPI of DSA value s. + else if (this.publicKeyAlgorithm == 17) + mpicount = 2; + + this.MPIs = new Array(); + for (var i = 0; i < mpicount; i++) { + this.MPIs[i] = new openpgp_type_mpi(); + if (this.MPIs[i].read(input, mypos, (mypos-position)) != null && + !this.packetLength < (mypos-position)) { + mypos += this.MPIs[i].packetLength; + } else { + util.print_error('signature contains invalid MPI @:'+mypos); + } + } + break; + case 4: + this.signatureType = input[mypos++].charCodeAt(); + this.publicKeyAlgorithm = input[mypos++].charCodeAt(); + this.hashAlgorithm = input[mypos++].charCodeAt(); + + // Two-octet scalar octet count for following hashed subpacket + // data. + var hashed_subpacket_count = (input[mypos++].charCodeAt() << 8) + input[mypos++].charCodeAt(); + + // Hashed subpacket data set (zero or more subpackets) + var subpacket_length = 0; + while (hashed_subpacket_count != subpacket_length) { + if (hashed_subpacket_count < subpacket_length) { + util.print_debug("openpgp.packet.signature.js\n"+"hashed missed something: "+mypos+" c:"+hashed_subpacket_count+" l:"+subpacket_length); + } + + subpacket_length += this._raw_read_signature_sub_packet(input, + mypos + subpacket_length, hashed_subpacket_count + - subpacket_length); + } + + mypos += hashed_subpacket_count; + this.signatureData = input.substring(position, mypos); + + // alert("signatureData: "+util.hexstrdump(this.signatureData)); + + // Two-octet scalar octet count for the following unhashed subpacket + var subpacket_count = (input[mypos++].charCodeAt() << 8) + input[mypos++].charCodeAt(); + + // Unhashed subpacket data set (zero or more subpackets). + subpacket_length = 0; + while (subpacket_count != subpacket_length) { + if (subpacket_count < subpacket_length) { + util.print_debug("openpgp.packet.signature.js\n"+"missed something: "+subpacket_length+" c:"+subpacket_count+" "+" l:"+subpacket_length); + } + subpacket_length += this._raw_read_signature_sub_packet(input, + mypos + subpacket_length, subpacket_count + - subpacket_length); + + } + mypos += subpacket_count; + // Two-octet field holding the left 16 bits of the signed hash + // value. + this.signedHashValue = (input[mypos++].charCodeAt() << 8) | input[mypos++].charCodeAt(); + // One or more multiprecision integers comprising the signature. + // This portion is algorithm specific, as described above. + var mpicount = 0; + if (this.publicKeyAlgorithm > 0 && this.publicKeyAlgorithm < 4) + mpicount = 1; + else if (this.publicKeyAlgorithm == 17) + mpicount = 2; + + this.MPIs = new Array(); + for (var i = 0; i < mpicount; i++) { + this.MPIs[i] = new openpgp_type_mpi(); + if (this.MPIs[i].read(input, mypos, (mypos-position)) != null && + !this.packetLength < (mypos-position)) { + mypos += this.MPIs[i].packetLength; + } else { + util.print_error('signature contains invalid MPI @:'+mypos); + } + } + break; + default: + util.print_error("openpgp.packet.signature.js\n"+'unknown signature packet version'+this.version); + break; + } + // util.print_message("openpgp.packet.signature.js\n"+"end signature: l: "+this.packetLength+"m: "+mypos+" m-p: "+(mypos-position)); + return this; + } + /** + * creates a string representation of a message signature packet (tag 2). + * This can be only used on text data + * @param signature_type [integer] should be 1 (one) + * @param data [String] data to be signed + * @param privatekey [openpgp_msg_privatekey] private key used to sign the message. (secMPIs MUST be unlocked) + * @return string representation of a signature packet + */ + function write_message_signature(signature_type, data, privatekey) { + var publickey = privatekey.privateKeyPacket.publicKey; + var hash_algo = privatekey.getPreferredSignatureHashAlgorithm(); + var result = String.fromCharCode(4); + result += String.fromCharCode(signature_type); + result += String.fromCharCode(publickey.publicKeyAlgorithm); + result += String.fromCharCode(hash_algo); + var d = Math.round(new Date().getTime() / 1000); + var datesubpacket = write_sub_signature_packet(2,""+ + String.fromCharCode((d >> 24) & 0xFF) + + String.fromCharCode((d >> 16) & 0xFF) + + String.fromCharCode((d >> 8) & 0xFF) + + String.fromCharCode(d & 0xFF)); + var issuersubpacket = write_sub_signature_packet(16, privatekey.getKeyId()); + result += String.fromCharCode(((datesubpacket.length + issuersubpacket.length) >> 8) & 0xFF); + result += String.fromCharCode ((datesubpacket.length + issuersubpacket.length) & 0xFF); + result += datesubpacket; + result += issuersubpacket; + var trailer = ''; + + trailer += String.fromCharCode(4); + trailer += String.fromCharCode(0xFF); + trailer += String.fromCharCode((result.length) >> 24); + trailer += String.fromCharCode(((result.length) >> 16) & 0xFF); + trailer += String.fromCharCode(((result.length) >> 8) & 0xFF); + trailer += String.fromCharCode((result.length) & 0xFF); + var result2 = String.fromCharCode(0); + result2 += String.fromCharCode(0); + var hash = openpgp_crypto_hashData(hash_algo, data+result+trailer); + util.print_debug("DSA Signature is calculated with:|"+data+result+trailer+"|\n"+util.hexstrdump(data+result+trailer)+"\n hash:"+util.hexstrdump(hash)); + result2 += hash.charAt(0); + result2 += hash.charAt(1); + result2 += openpgp_crypto_signData(hash_algo,privatekey.privateKeyPacket.publicKey.publicKeyAlgorithm, + publickey.MPIs, + privatekey.privateKeyPacket.secMPIs, + data+result+trailer); + return {openpgp: (openpgp_packet.write_packet_header(2, (result+result2).length)+result + result2), + hash: util.get_hashAlgorithmString(hash_algo)}; + } + /** + * creates a string representation of a sub signature packet (See RFC 4880 5.2.3.1) + * @param type [integer] subpacket signature type. Signature types as described in RFC4880 Section 5.2.3.2 + * @param data [String] data to be included + * @return [String] a string-representation of a sub signature packet (See RFC 4880 5.2.3.1) + */ + function write_sub_signature_packet(type, data) { + var result = ""; + result += openpgp_packet.encode_length(data.length+1); + result += String.fromCharCode(type); + result += data; + return result; + } + + // V4 signature sub packets + + this._raw_read_signature_sub_packet = function(input, position, len) { + if (len < 0) + util.print_debug("openpgp.packet.signature.js\n"+"_raw_read_signature_sub_packet length < 0 @:"+position); + var mypos = position; + var subplen = 0; + // alert('starting signature subpackage read at position:'+position+' length:'+len); + if (input[mypos].charCodeAt() < 192) { + subplen = input[mypos++].charCodeAt(); + } else if (input[mypos].charCodeAt() >= 192 && input[mypos].charCodeAt() < 224) { + subplen = ((input[mypos++].charCodeAt() - 192) << 8) + (input[mypos++].charCodeAt()) + 192; + } else if (input[mypos].charCodeAt() > 223 && input[mypos].charCodeAt() < 255) { + subplen = 1 << (input[mypos++].charCodeAt() & 0x1F); + } else if (input[mypos].charCodeAt() < 255) { + mypos++; + subplen = (input[mypos++].charCodeAt() << 24) | (input[mypos++].charCodeAt() << 16) + | (input[mypos++].charCodeAt() << 8) | input[mypos++].charCodeAt(); + } + + var type = input[mypos++].charCodeAt() & 0x7F; + // alert('signature subpacket type '+type+" with length: "+subplen); + // subpacket type + switch (type) { + case 2: // Signature Creation Time + this.creationTime = new Date(((input[mypos++].charCodeAt() << 24) | (input[mypos++].charCodeAt() << 16) + | (input[mypos++].charCodeAt() << 8) | input[mypos++].charCodeAt())*1000); + break; + case 3: // Signature Expiration Time + this.signatureExpirationTime = (input[mypos++].charCodeAt() << 24) + | (input[mypos++].charCodeAt() << 16) | (input[mypos++].charCodeAt() << 8) + | input[mypos++].charCodeAt(); + this.signatureNeverExpires = (this.signature_expiration_time == 0); + + break; + case 4: // Exportable Certification + this.exportable = input[mypos++].charCodeAt() == 1; + break; + case 5: // Trust Signature + this.trustLevel = input[mypos++].charCodeAt(); + this.trustAmount = input[mypos++].charCodeAt(); + break; + case 6: // Regular Expression + this.regular_expression = new String(); + for (var i = 0; i < subplen - 1; i++) + this.regular_expression += (input[mypos++]); + break; + case 7: // Revocable + this.revocable = input[mypos++].charCodeAt() == 1; + break; + case 9: // Key Expiration Time + this.keyExpirationTime = (input[mypos++].charCodeAt() << 24) + | (input[mypos++].charCodeAt() << 16) | (input[mypos++].charCodeAt() << 8) + | input[mypos++].charCodeAt(); + this.keyNeverExpires = (this.keyExpirationTime == 0); + break; + case 11: // Preferred Symmetric Algorithms + this.preferredSymmetricAlgorithms = new Array(); + for (var i = 0; i < subplen-1; i++) { + this.preferredSymmetricAlgorithms = input[mypos++].charCodeAt(); + } + break; + case 12: // Revocation Key + // (1 octet of class, 1 octet of public-key algorithm ID, 20 + // octets of + // fingerprint) + this.revocationKeyClass = input[mypos++].charCodeAt(); + this.revocationKeyAlgorithm = input[mypos++].charCodeAt(); + this.revocationKeyFingerprint = new Array(); + for ( var i = 0; i < 20; i++) { + this.revocationKeyFingerprint = input[mypos++].charCodeAt(); + } + break; + case 16: // Issuer + this.issuerKeyId = input.substring(mypos,mypos+8); + mypos += 8; + break; + case 20: // Notation Data + this.notationFlags = (input[mypos++].charCodeAt() << 24) | + (input[mypos++].charCodeAt() << 16) | + (input[mypos++].charCodeAt() << 8) | + (input[mypos++].charCodeAt()); + var nameLength = (input[mypos++].charCodeAt() << 8) | (input[mypos++].charCodeAt()); + var valueLength = (input[mypos++].charCodeAt() << 8) | (input[mypos++].charCodeAt()); + this.notationName = ""; + for (var i = 0; i < nameLength; i++) { + this.notationName += input[mypos++]; + } + this.notationValue = ""; + for (var i = 0; i < valueLength; i++) { + this.notationValue += input[mypos++]; + } + break; + case 21: // Preferred Hash Algorithms + this.preferredHashAlgorithms = new Array(); + for (var i = 0; i < subplen-1; i++) { + this.preferredHashAlgorithms = input[mypos++].charCodeAt(); + } + break; + case 22: // Preferred Compression Algorithms + this.preferredCompressionAlgorithms = new Array(); + for ( var i = 0; i < subplen-1; i++) { + this.preferredCompressionAlgorithms = input[mypos++].charCodeAt(); + } + break; + case 23: // Key Server Preferences + this.keyServerPreferences = new Array(); + for ( var i = 0; i < subplen-1; i++) { + this.keyServerPreferences = input[mypos++].charCodeAt(); + } + break; + case 24: // Preferred Key Server + this.preferredKeyServer = new String(); + for ( var i = 0; i < subplen-1; i++) { + this.preferredKeyServer += input[mypos++]; + } + break; + case 25: // Primary User ID + this.isPrimaryUserID = input[mypos++] != 0; + break; + case 26: // Policy URI + this.policyURI = new String(); + for ( var i = 0; i < subplen-1; i++) { + this.policyURI += input[mypos++]; + } + break; + case 27: // Key Flags + this.keyFlags = new Array(); + for ( var i = 0; i < subplen-1; i++) { + this.keyFlags = input[mypos++].charCodeAt(); + } + break; + case 28: // Signer's User ID + this.signersUserId = new String(); + for ( var i = 0; i < subplen-1; i++) { + this.signersUserId += input[mypos++]; + } + break; + case 29: // Reason for Revocation + this.reasonForRevocationFlag = input[mypos++].charCodeAt(); + this.reasonForRevocationString = new String(); + for ( var i = 0; i < subplen -2; i++) { + this.reasonForRevocationString += input[mypos++]; + } + break; + case 30: // Features + // TODO: to be implemented + return subplen+1; + case 31: // Signature Target + // (1 octet public-key algorithm, 1 octet hash algorithm, N octets hash) + this.signatureTargetPublicKeyAlgorithm = input[mypos++].charCodeAt(); + this.signatureTargetHashAlgorithm = input[mypos++].charCodeAt(); + var signatureTargetHashAlgorithmLength = 0; + switch(this.signatureTargetHashAlgorithm) { + case 1: // - MD5 [HAC] "MD5" + case 2: // - SHA-1 [FIPS180] "SHA1" + signatureTargetHashAlgorithmLength = 20; + break; + case 3: // - RIPE-MD/160 [HAC] "RIPEMD160" + case 8: // - SHA256 [FIPS180] "SHA256" + case 9: // - SHA384 [FIPS180] "SHA384" + case 10: // - SHA512 [FIPS180] "SHA512" + case 11: // - SHA224 [FIPS180] "SHA224" + break; + // 100 to 110 - Private/Experimental algorithm + default: + util.print_error("openpgp.packet.signature.js\n"+"unknown signature target hash algorithm:"+this.signatureTargetHashAlgorithm); + return null; + } + this.signatureTargetHash = new Array(); + for (var i = 0; i < signatureTargetHashAlgorithmLength; i++) { + this.signatureTargetHash[i] = input[mypos++]; + } + case 32: // Embedded Signature + this.embeddedSignature = new openpgp_packet_signature(); + this.embeddedSignature.read_packet(input, mypos, len -(mypos-position)); + return ((mypos+ this.embeddedSignature.packetLength) - position); + break; + case 100: // Private or experimental + case 101: // Private or experimental + case 102: // Private or experimental + case 103: // Private or experimental + case 104: // Private or experimental + case 105: // Private or experimental + case 106: // Private or experimental + case 107: // Private or experimental + case 108: // Private or experimental + case 109: // Private or experimental + case 110: // Private or experimental + return subplen+1; + util.print_error("openpgp.packet.signature.js\n"+'private or experimental signature subpacket type '+type+" @:"+mypos+" subplen:"+subplen+" len:"+len); + break; + case 0: // Reserved + case 1: // Reserved + case 8: // Reserved + case 10: // Placeholder for backward compatibility + case 13: // Reserved + case 14: // Reserved + case 15: // Reserved + case 17: // Reserved + case 18: // Reserved + case 19: // Reserved + default: + util.print_error("openpgp.packet.signature.js\n"+'unknown signature subpacket type '+type+" @:"+mypos+" subplen:"+subplen+" len:"+len); + return subplen+1; + break; + } + return mypos -position; + }; + /** + * verifys the signature packet. Note: not signature types are implemented + * @param data [String] data which on the signature applies + * @param key [openpgp_msg_privatekey] the public key to verify the signature + * @return + */ + function verify(data, key) { + switch(this.signatureType) { + // calculating the trailer + case 0: // 0x00: Signature of a binary document. + if (this.version == 4) { + var trailer = ''; + trailer += String.fromCharCode(this.version); + trailer += String.fromCharCode(0xFF); + trailer += String.fromCharCode(this.signatureData.length >> 24); + trailer += String.fromCharCode((this.signatureData.length >> 16) &0xFF); + trailer += String.fromCharCode((this.signatureData.length >> 8) &0xFF); + trailer += String.fromCharCode(this.signatureData.length & 0xFF); + return openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm, + this.MPIs, key.obj.publicKeyPacket.MPIs, data.substring(i)+this.signatureData+trailer); + } else if (this.version == 3) { + return false; + } + + case 1: // 0x01: Signature of a canonical text document. + if (this.version == 4) { + var trailer = ''; + trailer += String.fromCharCode(this.version); + trailer += String.fromCharCode(0xFF); + trailer += String.fromCharCode(this.signatureData.length >> 24); + trailer += String.fromCharCode((this.signatureData.length >> 16) &0xFF); + trailer += String.fromCharCode((this.signatureData.length >> 8) &0xFF); + trailer += String.fromCharCode(this.signatureData.length &0xFF); + return openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm, + this.MPIs, key.obj.publicKeyPacket.MPIs, data+this.signatureData+trailer); + } else if (this.version == 3) { + return false; + } + + case 2: // 0x02: Standalone signature. + // This signature is a signature of only its own subpacket contents. + // It is calculated identically to a signature over a zero-length + // binary document. Note that it doesn't make sense to have a V3 + // standalone signature. + if (this.version == 3) + return false; + + var trailer = ''; + trailer += String.fromCharCode(this.version); + trailer += String.fromCharCode(0xFF); + trailer += String.fromCharCode(this.signatureData.length >> 24); + trailer += String.fromCharCode((this.signatureData.length >> 16) &0xFF); + trailer += String.fromCharCode((this.signatureData.length >> 8) &0xFF); + trailer += String.fromCharCode(this.signatureData.length &0xFF); + return openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm, + this.MPIs, key.obj.publicKeyPacket.MPIs, this.signatureData+trailer); + case 16: + // 0x10: Generic certification of a User ID and Public-Key packet. + // The issuer of this certification does not make any particular + // assertion as to how well the certifier has checked that the owner + // of the key is in fact the person described by the User ID. + case 17: + // 0x11: Persona certification of a User ID and Public-Key packet. + // The issuer of this certification has not done any verification of + // the claim that the owner of this key is the User ID specified. + case 18: + // 0x12: Casual certification of a User ID and Public-Key packet. + // The issuer of this certification has done some casual + // verification of the claim of identity. + case 19: + // 0x13: Positive certification of a User ID and Public-Key packet. + // The issuer of this certification has done substantial + // verification of the claim of identity. + // + // Most OpenPGP implementations make their "key signatures" as 0x10 + // certifications. Some implementations can issue 0x11-0x13 + // certifications, but few differentiate between the types. + case 48: + // 0x30: Certification revocation signature + // This signature revokes an earlier User ID certification signature + // (signature class 0x10 through 0x13) or direct-key signature + // (0x1F). It should be issued by the same key that issued the + // revoked signature or an authorized revocation key. The signature + // is computed over the same data as the certificate that it + // revokes, and should have a later creation date than that + // certificate. + + var trailer = ''; + trailer += String.fromCharCode(this.version); + trailer += String.fromCharCode(0xFF); + trailer += String.fromCharCode(this.signatureData.length >> 24); + trailer += String.fromCharCode((this.signatureData.length >> 16) &0xFF); + trailer += String.fromCharCode((this.signatureData.length >> 8) &0xFF); + trailer += String.fromCharCode(this.signatureData.length &0xFF); + return openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm, + this.MPIs, key.MPIs, data+this.signatureData+trailer); + + case 24: + // 0x18: Subkey Binding Signature + // This signature is a statement by the top-level signing key that + // indicates that it owns the subkey. This signature is calculated + // directly on the primary key and subkey, and not on any User ID or + // other packets. A signature that binds a signing subkey MUST have + // an Embedded Signature subpacket in this binding signature that + // contains a 0x19 signature made by the signing subkey on the + // primary key and subkey. + if (this.version == 3) + return false; + + var trailer = ''; + trailer += String.fromCharCode(this.version); + trailer += String.fromCharCode(0xFF); + trailer += String.fromCharCode(this.signatureData.length >> 24); + trailer += String.fromCharCode((this.signatureData.length >> 16) &0xFF); + trailer += String.fromCharCode((this.signatureData.length >> 8) &0xFF); + trailer += String.fromCharCode(this.signatureData.length &0xFF); + return openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm, + this.MPIs, key.MPIs, data+this.signatureData+trailer); + case 25: + // 0x19: Primary Key Binding Signature + // This signature is a statement by a signing subkey, indicating + // that it is owned by the primary key and subkey. This signature + // is calculated the same way as a 0x18 signature: directly on the + // primary key and subkey, and not on any User ID or other packets. + + // When a signature is made over a key, the hash data starts with the + // octet 0x99, followed by a two-octet length of the key, and then body + // of the key packet. (Note that this is an old-style packet header for + // a key packet with two-octet length.) A subkey binding signature + // (type 0x18) or primary key binding signature (type 0x19) then hashes + // the subkey using the same format as the main key (also using 0x99 as + // the first octet). + case 31: + // 0x1F: Signature directly on a key + // This signature is calculated directly on a key. It binds the + // information in the Signature subpackets to the key, and is + // appropriate to be used for subpackets that provide information + // about the key, such as the Revocation Key subpacket. It is also + // appropriate for statements that non-self certifiers want to make + // about the key itself, rather than the binding between a key and a + // name. + case 32: + // 0x20: Key revocation signature + // The signature is calculated directly on the key being revoked. A + // revoked key is not to be used. Only revocation signatures by the + // key being revoked, or by an authorized revocation key, should be + // considered valid revocation signatures. + case 40: + // 0x28: Subkey revocation signature + // The signature is calculated directly on the subkey being revoked. + // A revoked subkey is not to be used. Only revocation signatures + // by the top-level signature key that is bound to this subkey, or + // by an authorized revocation key, should be considered valid + // revocation signatures. + var trailer = ''; + trailer += String.fromCharCode(this.version); + trailer += String.fromCharCode(0xFF); + trailer += String.fromCharCode(this.signatureData.length >> 24); + trailer += String.fromCharCode((this.signatureData.length >> 16) &0xFF); + trailer += String.fromCharCode((this.signatureData.length >> 8) &0xFF); + trailer += String.fromCharCode(this.signatureData.length &0xFF); + return openpgp_crypto_verifySignature(this.publicKeyAlgorithm, this.hashAlgorithm, + this.MPIs, key.MPIs, data+this.signatureData+trailer); + + + // Key revocation signatures (types 0x20 and 0x28) + // hash only the key being revoked. + case 64: + // 0x40: Timestamp signature. + // This signature is only meaningful for the timestamp contained in + // it. + case 80: + // 0x50: Third-Party Confirmation signature. + // This signature is a signature over some other OpenPGP Signature + // packet(s). It is analogous to a notary seal on the signed data. + // A third-party signature SHOULD include Signature Target + // subpacket(s) to give easy identification. Note that we really do + // mean SHOULD. There are plausible uses for this (such as a blind + // party that only sees the signature, not the key or source + // document) that cannot include a target subpacket. + default: + util.print_error("openpgp.packet.signature.js\n"+"signature verification for type"+ this.signatureType+" not implemented"); + break; + } + } + /** + * generates debug output (pretty print) + * @return String which gives some information about the signature packet + */ + + function toString () { + if (this.version == 3) { + var result = '5.2. Signature Packet (Tag 2)\n'+ + "Packet Length: :"+this.packetLength+'\n'+ + "Packet version: :"+this.version+'\n'+ + "One-octet signature type :"+this.signatureType+'\n'+ + "Four-octet creation time. :"+this.creationTime+'\n'+ + "Eight-octet Key ID of signer. :"+util.hexidump(this.keyId)+'\n'+ + "One-octet public-key algorithm. :"+this.publicKeyAlgorithm+'\n'+ + "One-octet hash algorithm. :"+this.hashAlgorithm+'\n'+ + "Two-octet field holding left\n" + + " 16 bits of signed hash value. :"+this.signedHashValue+'\n'; + } else { + var result = '5.2. Signature Packet (Tag 2)\n'+ + "Packet Length: :"+this.packetLength+'\n'+ + "Packet version: :"+this.version+'\n'+ + "One-octet signature type :"+this.signatureType+'\n'+ + "One-octet public-key algorithm. :"+this.publicKeyAlgorithm+'\n'+ + "One-octet hash algorithm. :"+this.hashAlgorithm+'\n'+ + "Two-octet field holding left\n" + + " 16 bits of signed hash value. :"+this.signedHashValue+'\n'+ + "Signature Creation Time :"+this.creationTime+'\n'+ + "Signature Expiration Time :"+this.signatureExpirationTime+'\n'+ + "Signature Never Expires :"+this.signatureNeverExpires+'\n'+ + "Exportable Certification :"+this.exportable+'\n'+ + "Trust Signature level: :"+this.trustLevel+' amount'+this.trustAmount+'\n'+ + "Regular Expression :"+this.regular_expression+'\n'+ + "Revocable :"+this.revocable+'\n'+ + "Key Expiration Time :"+this.keyExpirationTime+" "+this.keyNeverExpires+'\n'+ + "Preferred Symmetric Algorithms :"+this.preferredSymmetricAlgorithms+'\n'+ + "Revocation Key"+'\n'+ + " ( 1 octet of class, :"+this.revocationKeyClass +'\n'+ + " 1 octet of public-key ID, :" +this.revocationKeyAlgorithm+'\n'+ + " 20 octets of fingerprint) :"+this.revocationKeyFingerprint+'\n'+ + "Issuer :"+util.hexstrdump(this.issuerKeyId)+'\n'+ + "Preferred Hash Algorithms :"+this.preferredHashAlgorithms+'\n'+ + "Preferred Compression Alg. :"+this.preferredCompressionAlgorithms+'\n'+ + "Key Server Preferences :"+this.keyServerPreferences+'\n'+ + "Preferred Key Server :"+this.preferredKeyServer+'\n'+ + "Primary User ID :"+this.isPrimaryUserID+'\n'+ + "Policy URI :"+this.policyURI+'\n'+ + "Key Flags :"+this.keyFlags+'\n'+ + "Signer's User ID :"+this.signersUserId+'\n'+ + "Notation :"+this.notationName+" = "+this.notationValue+"\n"+ + "Reason for Revocation\n"+ + " Flag :"+this.reasonForRevocationFlag+'\n'+ + " Reason :"+this.reasonForRevocationString+'\nMPI:\n'; + } + for (var i = 0; i < this.MPIs.length; i++) { + result += this.MPIs[i].toString(); + } + return result; + } + + /** + * gets the issuer key id of this signature + * @return [String] issuer key id as string (8bytes) + */ + function getIssuer() { + if (this.version == 4) + return this.issuerKeyId; + if (this.verions == 4) + return this.keyId; + return null; + } + + /** + * Tries to get the corresponding public key out of the public keyring for the issuer created this signature + * @return {obj: [openpgp_msg_publickey], text: [String]} if found the public key will be returned. null otherwise + */ + function getIssuerKey() { + var result = null; + if (this.version == 4) { + result = openpgp.keyring.getPublicKeysForKeyId(this.issuerKeyId); + } else if (this.version == 3) { + result = openpgp.keyring.getPublicKeysForKeyId(this.keyId); + } else return null; + if (result.length == 0) + return null; + return result[0]; + } + this.getIssuerKey = getIssuerKey; + this.getIssuer = getIssuer; + this.write_message_signature = write_message_signature; + this.verify = verify; + this.read_packet = read_packet; + this.toString = toString; +} \ No newline at end of file diff --git a/src/packet/openpgp.packet.userattribute.js b/src/packet/openpgp.packet.userattribute.js new file mode 100755 index 00000000..7bf364fd --- /dev/null +++ b/src/packet/openpgp.packet.userattribute.js @@ -0,0 +1,153 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * The User Attribute packet is a variation of the User ID packet. It + * is capable of storing more types of data than the User ID packet, + * which is limited to text. Like the User ID packet, a User Attribute + * packet may be certified by the key owner ("self-signed") or any other + * key owner who cares to certify it. Except as noted, a User Attribute + * packet may be used anywhere that a User ID packet may be used. + * + * While User Attribute packets are not a required part of the OpenPGP + * standard, implementations SHOULD provide at least enough + * compatibility to properly handle a certification signature on the + * User Attribute packet. A simple way to do this is by treating the + * User Attribute packet as a User ID packet with opaque contents, but + * an implementation may use any method desired. + */ +function openpgp_packet_userattribute() { + this.tagType = 17; + this.certificationSignatures = new Array(); + this.certificationRevocationSignatures = new Array(); + this.revocationSignatures = new Array(); + this.parentNode = null; + + /** + * parsing function for a user attribute packet (tag 17). + * @param input [string] payload of a tag 17 packet + * @param position [integer] position to start reading from the input string + * @param len [integer] length of the packet or the remaining length of input at position + * @return [openpgp_packet_encrypteddata] object representation + */ + function read_packet (input, position, len) { + var total_len = 0; + this.packetLength = len; + this.userattributes = new Array(); + var count = 0; + var mypos = position; + while (len != total_len) { + var current_len = 0; + // 4.2.2.1. One-Octet Lengths + if (input[mypos].charCodeAt() < 192) { + packet_length = input[mypos++].charCodeAt(); + current_len = 1; + // 4.2.2.2. Two-Octet Lengths + } else if (input[mypos].charCodeAt() >= 192 && input[mypos].charCodeAt() < 224) { + packet_length = ((input[mypos++].charCodeAt() - 192) << 8) + + (input[mypos++].charCodeAt()) + 192; + current_len = 2; + // 4.2.2.4. Partial Body Lengths + } else if (input[mypos].charCodeAt() > 223 && input[mypos].charCodeAt() < 255) { + packet_length = 1 << (input[mypos++].charCodeAt() & 0x1F); + current_len = 1; + // 4.2.2.3. Five-Octet Lengths + } else { + current_len = 5; + mypos++; + packet_length = (input[mypos++].charCodeAt() << 24) | (input[mypos++].charCodeAt() << 16) + | (input[mypos++].charCodeAt() << 8) | input[mypos++].charCodeAt(); + } + + var subpackettype = input[mypos++].charCodeAt(); + packet_length--; + current_len++; + this.userattributes[count] = new Array(); + this.userattributes[count] = input.substring(mypos, mypos + packet_length); + mypos += packet_length; + total_len += current_len+packet_length; + } + this.packetLength = mypos - position; + return this; + } + + /** + * generates debug output (pretty print) + * @return String which gives some information about the user attribute packet + */ + function toString() { + var result = '5.12. User Attribute Packet (Tag 17)\n'+ + ' AttributePackets: (count = '+this.userattributes.length+')\n'; + for (var i = 0; i < this.userattributes.length; i++) { + result += ' ('+this.userattributes[i].length+') bytes: ['+util.hexidump(this.userattributes[i])+']\n'; + } + return result; + } + + /** + * Continue parsing packets belonging to the user attribute packet such as signatures + * @param parent_node [openpgp_*] the parent object + * @param input [String] input string to read the packet(s) from + * @param position [integer] start position for the parser + * @param len [integer] length of the packet(s) or remaining length of input + * @return [integer] length of nodes read + */ + function read_nodes(parent_node, input, position, len) { + + this.parentNode = parent_node; + var exit = false; + var pos = position; + var l = len; + while (input.length != pos) { + var result = openpgp_packet.read_packet(input, pos, l); + if (result == null) { + util.print_error("openpgp.packet.userattribute.js\n"+'[user_attr] parsing ends here @:' + pos + " l:" + l); + break; + } else { + switch (result.tagType) { + case 2: // Signature Packet + if (result.signatureType > 15 + && result.signatureType < 20) // certification + // // + // signature + this.certificationSignatures[this.certificationSignatures.length] = result; + else if (result.signatureType == 32) // certification revocation signature + this.certificationRevocationSignatures[this.certificationRevocationSignatures.length] = result; + pos += result.packetLength + result.headerLength; + l = len - (pos - position); + break; + default: + this.data = input; + this.position = position - parent_node.packetLength; + this.len = pos - position; + return this.len; + break; + } + } + } + this.data = input; + this.position = position - parent_node.packetLength; + this.len = pos - position; + return this.len; + + } + + this.read_packet = read_packet; + this.read_nodes = read_nodes; + this.toString = toString; + +}; \ No newline at end of file diff --git a/src/packet/openpgp.packet.userid.js b/src/packet/openpgp.packet.userid.js new file mode 100755 index 00000000..49025e3b --- /dev/null +++ b/src/packet/openpgp.packet.userid.js @@ -0,0 +1,333 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * A User ID packet consists of UTF-8 text that is intended to represent + * the name and email address of the key holder. By convention, it + * includes an RFC 2822 [RFC2822] mail name-addr, but there are no + * restrictions on its content. The packet length in the header + * specifies the length of the User ID. + */ + +function openpgp_packet_userid() { + this.tagType = 13; + this.certificationSignatures = new Array(); + this.certificationRevocationSignatures = new Array(); + this.revocationSignatures = new Array(); + this.parentNode = null; + + /** + * parsing function for a user id packet (tag 13). + * @param input [string] payload of a tag 13 packet + * @param position [integer] position to start reading from the input string + * @param len [integer] length of the packet or the remaining length of input at position + * @return [openpgp_packet_encrypteddata] object representation + */ + function read_packet(input, position, len) { + this.text = ''; + this.packetLength = len; + + for ( var i = 0; i < len; i++) { + this.text += input[position + i]; + } + return this; + } + + /** + * creates a string representation of the user id packet + * @param user_id [String] the user id as string ("John Doe 15 + && result.signatureType < 20) { // certification + // // + // signature + this.certificationSignatures[this.certificationSignatures.length] = result; + break; + } else if (result.signatureType == 48) {// certification revocation signature + this.certificationRevocationSignatures[this.certificationRevocationSignatures.length] = result; + break; + } else if (result.signatureType == 24) { // omg. standalone signature + this.certificationSignatures[this.certificationSignatures.length] = result; + break; + } else { + util.debug("unknown sig t: "+result.signatureType+"@"+(pos - (result.packetLength + result.headerLength))); + } + default: + this.data = input; + this.position = position - parent_node.packetLength; + this.len = pos - position -(result.headerLength + result.packetLength); + return this.len; + } + } + } + this.data = input; + this.position = position - parent_node.packetLength; + this.len = pos - position -(result.headerLength + result.packetLength); + return this.len; + } else if (parent_node.tagType == 5) { // secret Key + this.parentNode = parent_node; + var exit = false; + var pos = position; + while (input.length != pos) { + var result = openpgp_packet.read_packet(input, pos, l - (pos - position)); + if (result == null) { + util.print_error('parsing ends here @:' + pos + " l:" + l); + break; + } else { + pos += result.packetLength + result.headerLength; + l = input.length - pos; + switch (result.tagType) { + case 2: // Signature Packet certification signature + if (result.signatureType > 15 + && result.signatureType < 20) + this.certificationSignatures[this.certificationSignatures.length] = result; + // certification revocation signature + else if (result.signatureType == 48) + this.certificationRevocationSignatures[this.certificationRevocationSignatures.length] = result; + default: + this.data = input; + this.position = position - parent_node.packetLength; + this.len = pos - position -(result.headerLength + result.packetLength); + return this.len; + } + } + } + } else { + util.print_error("unknown parent node for a userId packet "+parent_node.tagType); + } + } + + /** + * generates debug output (pretty print) + * @return String which gives some information about the user id packet + */ + function toString() { + var result = ' 5.11. User ID Packet (Tag 13)\n' + ' text (' + + this.text.length + '): "' + this.text.replace("<", "<") + + '"\n'; + result +="certification signatures:\n"; + for (var i = 0; i < this.certificationSignatures.length; i++) { + result += " "+this.certificationSignatures[i].toString(); + } + result +="certification revocation signatures:\n"; + for (var i = 0; i < this.certificationRevocationSignatures.length; i++) { + result += " "+this.certificationRevocationSignatures[i].toString(); + } + return result; + } + + /** + * lookup function to find certification revocation signatures + * @param keyId string containing the key id of the issuer of this signature + * @return a CertificationRevocationSignature if found; otherwise null + */ + function hasCertificationRevocationSignature(keyId) { + for (var i = 0; i < this.certificationRevocationSignatures.length; i++) { + if ((this.certificationRevocationSignatures[i].version == 3 && + this.certificationRevocationSignatures[i].keyId == keyId) || + (this.certificationRevocationSignatures[i].version == 4 && + this.certificationRevocationSignatures[i].issuerKeyId == keyId)) + return this.certificationRevocationSignatures[i]; + } + return null; + } + + /** + * Verifies all certification signatures. This method does not consider possible revocation signatures. + * @param publicKeyPacket the top level key material + * @return an array of integers corresponding to the array of certification signatures. The meaning of each integer is the following: + * 0 = bad signature + * 1 = signature expired + * 2 = issuer key not available + * 3 = revoked + * 4 = signature valid + * 5 = signature by key owner expired + * 6 = signature by key owner revoked + */ + function verifyCertificationSignatures(publicKeyPacket) { + result = new Array(); + for (var i = 0 ; i < this.certificationSignatures.length; i++) { + // A certification signature (type 0x10 through 0x13) hashes the User + // ID being bound to the key into the hash context after the above + // data. A V3 certification hashes the contents of the User ID or + // attribute packet packet, without any header. A V4 certification + // hashes the constant 0xB4 for User ID certifications or the constant + // 0xD1 for User Attribute certifications, followed by a four-octet + // number giving the length of the User ID or User Attribute data, and + // then the User ID or User Attribute data. + + if (this.certificationSignatures[i].version == 4) { + if (this.certificationSignatures[i].signatureExpirationTime != null && + this.certificationSignatures[i].signatureExpirationTime != null && + this.certificationSignatures[i].signatureExpirationTime != 0 && + !this.certificationSignatures[i].signatureNeverExpires && + new Date(this.certificationSignatures[i].creationTime.getTime() +(this.certificationSignatures[i].signatureExpirationTime*1000)) < new Date()) { + if (this.certificationSignatures[i].issuerKeyId == publicKeyPacket.getKeyId()) + result[i] = 5; + else + result[i] = 1; + continue; + } + if (this.certificationSignatures[i].issuerKeyId == null) { + result[i] = 0; + continue; + } + var issuerPublicKey = openpgp.keyring.getPublicKeysForKeyId(this.certificationSignatures[i].issuerKeyId); + if (issuerPublicKey == null || issuerPublicKey.length == 0) { + result[i] = 2; + continue; + } + // TODO: try to verify all returned issuer public keys (key ids are not unique!) + var issuerPublicKey = issuerPublicKey[0]; + var signingKey = issuerPublicKey.obj.getSigningKey(); + if (signingKey == null) { + result[i] = 0; + continue; + } + var revocation = this.hasCertificationRevocationSignature(this.certificationSignatures[i].issuerKeyId); + if (revocation != null && revocation.creationTime > + this.certificationSignatures[i].creationTime) { + var signaturedata = String.fromCharCode(0x99)+ publicKeyPacket.header.substring(1)+ + publicKeyPacket.data+String.fromCharCode(0xB4)+ + String.fromCharCode((this.text.length >> 24) & 0xFF)+ + String.fromCharCode((this.text.length >> 16) & 0xFF)+ + String.fromCharCode((this.text.length >> 8) & 0xFF)+ + String.fromCharCode((this.text.length) & 0xFF)+ + this.text; + if (revocation.verify(signaturedata, signingKey)) { + if (this.certificationSignatures[i].issuerKeyId == publicKeyPacket.getKeyId()) + result[i] = 6; + else + result[i] = 3; + continue; + } + } + var signaturedata = String.fromCharCode(0x99)+ publicKeyPacket.header.substring(1)+ + publicKeyPacket.data+String.fromCharCode(0xB4)+ + String.fromCharCode((this.text.length >> 24) & 0xFF)+ + String.fromCharCode((this.text.length >> 16) & 0xFF)+ + String.fromCharCode((this.text.length >> 8) & 0xFF)+ + String.fromCharCode((this.text.length) & 0xFF)+ + this.text; + if (this.certificationSignatures[i].verify(signaturedata, signingKey)) { + result[i] = 4; + } else + result[i] = 0; + } else if (this.certificationSignatures[i].version == 3) { + if (this.certificationSignatures[i].keyId == null) { + result[i] = 0; + continue; + } + var issuerPublicKey = openpgp.keyring.getPublicKeysForKeyId(this.certificationSignatures[i].keyId); + if (issuerPublicKey == null || issuerPublicKey.length == 0) { + result[i] = 2; + continue; + } + issuerPublicKey = issuerPublicKey[0]; + var signingKey = publicKey.obj.getSigningKey(); + if (signingKey == null) { + result[i] = 0; + continue; + } + var revocation = this.hasCertificationRevocationSignature(this.certificationSignatures[i].keyId); + if (revocation != null && revocation.creationTime > + this.certificationSignatures[i].creationTime) { + var signaturedata = String.fromCharCode(0x99)+ this.publicKeyPacket.header.substring(1)+ + this.publicKeyPacket.data+this.text; + if (revocation.verify(signaturedata, signingKey)) { + if (revocation.keyId == publicKeyPacket.getKeyId()) + result[i] = 6; + else + result[i] = 3; + continue; + } + } + var signaturedata = String.fromCharCode(0x99)+ publicKeyPacket.header.substring(1)+ + publicKeyPacket.data+this.text; + if (this.certificationSignatures[i].verify(signaturedata, signingKey)) { + result[i] = 4; + } else + result[i] = 0; + } else { + result[i] = 0; + } + } + return result; + } + + /** + * verifies the signatures of the user id + * @return 0 if the userid is valid; 1 = userid expired; 2 = userid revoked + */ + function verify(publicKeyPacket) { + var result = this.verifyCertificationSignatures(publicKeyPacket); + if (result.indexOf(6) != -1) + return 2; + if (result.indexOf(5) != -1) + return 1; + return 0; + } + + // TODO: implementation missing + function addCertification(publicKeyPacket, privateKeyPacket) { + + } + + // TODO: implementation missing + function revokeCertification(publicKeyPacket, privateKeyPacket) { + + } + + this.hasCertificationRevocationSignature = hasCertificationRevocationSignature; + this.verifyCertificationSignatures = verifyCertificationSignatures; + this.verify = verify; + this.read_packet = read_packet; + this.toString = toString; + this.read_nodes = read_nodes; +} \ No newline at end of file diff --git a/src/type/openpgp.type.keyid.js b/src/type/openpgp.type.keyid.js new file mode 100755 index 00000000..36306272 --- /dev/null +++ b/src/type/openpgp.type.keyid.js @@ -0,0 +1,47 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * Implementation of type key id (RFC4880 3.3) + * A Key ID is an eight-octet scalar that identifies a key. + Implementations SHOULD NOT assume that Key IDs are unique. The + section "Enhanced Key Formats" below describes how Key IDs are + formed. + */ +function openpgp_type_keyid() { + /** + * parsing method for a key id + * @param input [String] input to read the key id from + * @param position [integer] position where to start reading the key id from input + * @return this object + */ + function read_packet(input, position) { + this.bytes = input.substring(position, position+8); + return this; + } + + /** + * generates debug output (pretty print) + * @return [String] Key Id as hexadecimal string + */ + function toString() { + return util.hexstrdump(this.bytes); + } + + this.read_packet = read_packet; + this.toString = toString; +}; diff --git a/src/type/openpgp.type.mpi.js b/src/type/openpgp.type.mpi.js new file mode 100755 index 00000000..48ed25a8 --- /dev/null +++ b/src/type/openpgp.type.mpi.js @@ -0,0 +1,132 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +// Hint: We hold our MPIs as an array of octets in big endian format preceeding a two +// octet scalar: MPI: [a,b,c,d,e,f] +// - MPI size: (a << 8) | b +// - MPI = c | d << 8 | e << ((MPI.length -2)*8) | f ((MPI.length -2)*8) + +/** + * Implementation of type MPI (RFC4880 3.2) + * Multiprecision integers (also called MPIs) are unsigned integers used + * to hold large integers such as the ones used in cryptographic + * calculations. + * An MPI consists of two pieces: a two-octet scalar that is the length + * of the MPI in bits followed by a string of octets that contain the + * actual integer. + */ +function openpgp_type_mpi() { + this.MPI = null; + this.mpiBitLength = null; + this.mpiByteLength = null; + this.data = null; + /** + * parsing function for a mpi (RFC 4880 3.2). + * @param input [string] payload of mpi data + * @param position [integer] position to start reading from the input string + * @param len [integer] length of the packet or the remaining length of input at position + * @return [openpgp_type_mpi] object representation + */ + function read(input, position, len) { + var mypos = position; + + this.mpiBitLength = (input[mypos++].charCodeAt() << 8) | input[mypos++].charCodeAt(); + + // Additional rules: + // + // The size of an MPI is ((MPI.length + 7) / 8) + 2 octets. + // + // The length field of an MPI describes the length starting from its + // most significant non-zero bit. Thus, the MPI [00 02 01] is not + // formed correctly. It should be [00 01 01]. + + // TODO: Verification of this size method! This size calculation as + // specified above is not applicable in JavaScript + this.mpiByteLength = (this.mpiBitLength - (this.mpiBitLength % 8)) / 8; + if (this.mpiBitLength % 8 != 0) + this.mpiByteLength++; + + this.MPI = input.substring(mypos,mypos+this.mpiByteLength); + this.data = input.substring(position, position+2+this.mpiByteLength); + this.packetLength = this.mpiByteLength +2; + return this; + } + + /** + * generates debug output (pretty print) + * @return String which gives some information about the mpi + */ + function toString() { + var r = " MPI("+this.mpiBitLength+"b/"+this.mpiByteLength+"B) : 0x"; + r+=util.hexstrdump(this.MPI); + return r+'\n'; + } + + /** + * converts the mpi to an BigInteger object + * @return [BigInteger] + */ + function getBigInteger() { + return new BigInteger(util.hexstrdump(this.MPI),16); + } + + + function getBits(num) { + for (var i = 0; i < 9; i++) + if (num >> i == 0) + return i; + } + + /** + * gets the length of the mpi in bytes + * @return [integer] mpi byte length + */ + function getByteLength() { + return this.mpiByteLength; + } + + /** + * creates an mpi from the specified string + * @param data [String] data to read the mpi from + * @return [openpgp_type_mpi] + */ + function create(data) { + this.MPI = data; + this.mpiBitLength = (data.length -1) *8 + getBits(data.charCodeAt(0)); + this.mpiByteLength = data.length; + return this; + } + + /** + * converts the mpi object to a string as specified in RFC4880 3.2 + * @return [String] mpi byte representation + */ + function toBin() { + var result = String.fromCharCode((this.mpiBitLength >> 8) & 0xFF); + result += String.fromCharCode(this.mpiBitLength & 0xFF); + result += this.MPI; + return result; + } + + this.read = read; + this.toBigInteger = getBigInteger; + this.toString = toString; + this.create = create; + this.toBin = toBin; + this.getByteLength = getByteLength; +} + diff --git a/src/type/openpgp.type.s2k.js b/src/type/openpgp.type.s2k.js new file mode 100755 index 00000000..80d441da --- /dev/null +++ b/src/type/openpgp.type.s2k.js @@ -0,0 +1,98 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +/** + * Implementation of the String-to-key specifier (RFC4880 3.7) + * String-to-key (S2K) specifiers are used to convert passphrase strings + into symmetric-key encryption/decryption keys. They are used in two + places, currently: to encrypt the secret part of private keys in the + private keyring, and to convert passphrases to encryption keys for + symmetrically encrypted messages. + */ +function openpgp_type_s2k() { + /** + * parsing function for a string-to-key specifier (RFC 4880 3.7). + * @param input [string] payload of string-to-key specifier + * @param position [integer] position to start reading from the input string + * @return [openpgp_type_s2k] object representation + */ + function read(input, position) { + var mypos = position; + this.type = input[mypos++].charCodeAt(); + switch (this.type) { + case 0: // Simple S2K + // Octet 1: hash algorithm + this.hashAlgorithm = input[mypos++].charCodeAt(); + this.s2kLength = 1; + break; + + case 1: // Salted S2K + // Octet 1: hash algorithm + this.hashAlgorithm = input[mypos++].charCodeAt(); + + // Octets 2-9: 8-octet salt value + this.saltValue = input.substring(mypos, mypos+8); + mypos += 8; + this.s2kLength = 9; + break; + + case 3: // Iterated and Salted S2K + // Octet 1: hash algorithm + this.hashAlgorithm = input[mypos++].charCodeAt(); + + // Octets 2-9: 8-octet salt value + this.saltValue = input.substring(mypos, mypos+8); + mypos += 8; + + // Octet 10: count, a one-octet, coded value + this.EXPBIAS = 6; + var c = input[mypos++].charCodeAt(); + this.count = (16 + (c & 15)) << ((c >> 4) + this.EXPBIAS); + this.s2kLength = 10; + break; + + case 2: // Reserved value + default: + util.print_error("unknown s2k type! "+this.type); + break; + } + return this; + } + + /** + * produces a key using the specified passphrase and the defined hashAlgorithm + * @param passphrase [String] passphrase containing user input + * @return [String] produced key with a length corresponding to hashAlgorithm hash length + */ + function produce_key(passphrase) { + if (this.type == 0) { + return openpgp_crypto_hashData(this.hashAlgorithm,passphrase); + } else if (this.type == 1) { + return openpgp_crypto_hashData(this.hashAlgorithm,this.saltValue+passphrase); + } else if (this.type == 3) { + var isp = this.saltValue+passphrase; + while (isp.length < this.count) + isp += this.saltValue+passphrase; + if (isp.length < this.count) + isp = isp.substr(0, this.count); + return openpgp_crypto_hashData(this.hashAlgorithm,isp); + } else return null; + } + + this.read = read; + this.produce_key = produce_key; +} diff --git a/src/util/util.js b/src/util/util.js new file mode 100755 index 00000000..decf7cdd --- /dev/null +++ b/src/util/util.js @@ -0,0 +1,217 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +var Util = function() { + + this.hexdump = function(str) { + var r=""; + var e=str.length; + var c=0; + var h; + var i = 0; + while(c' + * @param str [String] string of the debug message + * @return [String] an HTML tt entity containing a paragraph with a style attribute where the debug message is HTMLencoded in. + */ + this.print_debug = function(str) { + if (openpgp.config.debug) { + str = openpgp_encoding_html_encode(str); + showMessages("

"+str.replace(/\n/g,"
")+"

"); + } + }; + + /** + * Helper function to print an error message. + * The calling Javascript context MUST define + * a "showMessages(text)" function. Line feeds ('\n') + * are automatically converted to HTML line feeds '
' + * @param str [String] string of the error message + * @return [String] a HTML paragraph entity with a style attribute containing the HTML encoded error message + */ + this.print_error = function(str) { + str = openpgp_encoding_html_encode(str); + showMessages("

ERROR: "+str.replace(/\n/g,"
")+"

"); + }; + + /** + * Helper function to print an info message. + * The calling Javascript context MUST define + * a "showMessages(text)" function. Line feeds ('\n') + * are automatically converted to HTML line feeds '
'. + * @param str [String] string of the info message + * @return [String] a HTML paragraph entity with a style attribute containing the HTML encoded info message + */ + this.print_info = function(str) { + str = openpgp_encoding_html_encode(str); + showMessages("

INFO: "+str.replace(/\n/g,"
")+"

"); + }; + + this.print_warning = function(str) { + str = openpgp_encoding_html_encode(str); + showMessages("

WARNING: "+str.replace(/\n/g,"
")+"

"); + }; + + this.getLeftNBits = function (string, bitcount) { + var rest = bitcount % 8; + if (rest == 0) + return string.substring(0, bitcount / 8); + var bytes = (bitcount - rest) / 8 +1; + var result = string.substring(0, bytes); + return this.shiftRight(result, 8-rest); // +String.fromCharCode(string.charCodeAt(bytes -1) << (8-rest) & 0xFF); + }; + /** + * Shifting a string to n bits right + * @param value [String] the string to shift + * @param bitcount [Integer] amount of bits to shift (MUST be smaller than 9) + * @return [String] resulting string. + */ + this.shiftRight = function(value, bitcount) { + var temp = util.str2bin(value); + if (bitcount % 8 != 0) { + for (var i = temp.length-1; i >= 0; i--) { + temp[i] >>= bitcount % 8; + if (i > 0) + temp[i] |= (temp[i - 1] << (8 - (bitcount % 8))) & 0xFF; + } + } else { + return value; + } + return util.bin2str(temp); + }; + + /** + * Return the algorithm type as string + * @return [String] String representing the message type + */ + this.get_hashAlgorithmString = function(algo) { + switch(algo) { + case 1: + return "MD5"; + case 2: + return "SHA1"; + case 3: + return "RIPEMD160"; + case 8: + return "SHA256"; + case 9: + return "SHA384"; + case 10: + return "SHA512"; + case 11: + return "SHA224"; + } + return "unknown"; + }; +}; + +/** + * an instance that should be used. + */ +var util = new Util(); diff --git a/test/ciphers/hash/md5.js b/test/ciphers/hash/md5.js new file mode 100644 index 00000000..4fd6c254 --- /dev/null +++ b/test/ciphers/hash/md5.js @@ -0,0 +1,17 @@ + +unittests.register("MD5 test with test vectors from RFC 1321", function() { + var result = new Array(); + result[0] = new test_result("MD5 (\"\") = d41d8cd98f00b204e9800998ecf8427e", + util.hexstrdump(MD5("")) == "d41d8cd98f00b204e9800998ecf8427e"); + result[1] = new test_result("MD5 (\"a\") = 0cc175b9c0f1b6a831c399e269772661", + util.hexstrdump(MD5 ("abc")) == "900150983cd24fb0d6963f7d28e17f72"); + result[2] = new test_result("MD5 (\"message digest\") = f96b697d7cb7938d525a2f31aaf161d0", + util.hexstrdump(MD5 ("message digest")) == "f96b697d7cb7938d525a2f31aaf161d0"); + result[3] = new test_result("MD5 (\"abcdefghijklmnopqrstuvwxyz\") = c3fcd3d76192e4007dfb496cca67e13b", + util.hexstrdump(MD5 ("abcdefghijklmnopqrstuvwxyz")) == "c3fcd3d76192e4007dfb496cca67e13b"); + result[4] = new test_result("MD5 (\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\") = d174ab98d277d9f5a5611c2c9f419d9f", + util.hexstrdump(MD5 ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789")) == "d174ab98d277d9f5a5611c2c9f419d9f"); + result[5] = new test_result("MD5 (\"12345678901234567890123456789012345678901234567890123456789012345678901234567890\") = 57edf4a22be3c955ac49da2e2107b67a", + util.hexstrdump(MD5 ("12345678901234567890123456789012345678901234567890123456789012345678901234567890")) == "57edf4a22be3c955ac49da2e2107b67a"); + return result; +}); diff --git a/test/ciphers/hash/ripe-md.js b/test/ciphers/hash/ripe-md.js new file mode 100644 index 00000000..38fc1d18 --- /dev/null +++ b/test/ciphers/hash/ripe-md.js @@ -0,0 +1,13 @@ + +unittests.register("RIPE-MD 160 bits test with test vectors from http://homes.esat.kuleuven.be/~bosselae/ripemd160.html", function() { + var result = new Array(); + result[0] = new test_result("RMDstring (\"\") = 9c1185a5c5e9fc54612808977ee8f548b2258d31", + util.hexstrdump(RMDstring("")) == "9c1185a5c5e9fc54612808977ee8f548b2258d31"); + result[1] = new test_result("RMDstring (\"a\") = 0bdc9d2d256b3ee9daae347be6f4dc835a467ffe", + util.hexstrdump(RMDstring("a")) == "0bdc9d2d256b3ee9daae347be6f4dc835a467ffe"); + result[2] = new test_result("RMDstring (\"abc\") = 8eb208f7e05d987a9b044a8e98c6b087f15a0bfc", + util.hexstrdump(RMDstring("abc")) == "8eb208f7e05d987a9b044a8e98c6b087f15a0bfc"); + result[3] = new test_result("RMDstring (\"message digest\") = 5d0689ef49d2fae572b881b123a85ffa21595f36", + util.hexstrdump(RMDstring("message digest")) == "5d0689ef49d2fae572b881b123a85ffa21595f36"); + return result; +}); diff --git a/test/ciphers/hash/sha.js b/test/ciphers/hash/sha.js new file mode 100644 index 00000000..be3a28cd --- /dev/null +++ b/test/ciphers/hash/sha.js @@ -0,0 +1,26 @@ + +unittests.register("SHA* test with test vectors from NIST FIPS 180-2", function() { + var result = new Array(); + + result[0] = new test_result("SHA1 - a9993e364706816aba3e25717850c26c9cd0d89d = str_sha1(\"abc\") ", + "a9993e364706816aba3e25717850c26c9cd0d89d" == util.hexstrdump(str_sha1("abc"))); + result[1] = new test_result("SHA1 - 84983e441c3bd26ebaae4aa1f95129e5e54670f1 = str_sha1(\"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq\") ", + "84983e441c3bd26ebaae4aa1f95129e5e54670f1" == util.hexstrdump(str_sha1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"))); + result[2] = new test_result("SHA224 - 23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7 = str_sha224(\"abc\") ", + "23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7" == util.hexstrdump(str_sha224("abc"))); + result[3] = new test_result("SHA224 - 75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525 = str_sha224(\"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq\") ", + "75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525" == util.hexstrdump(str_sha224("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"))); + result[4] = new test_result("SHA256 - ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad = str_sha256(\"abc\") ", + "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" == util.hexstrdump(str_sha256("abc"))); + result[5] = new test_result("SHA256 - 248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1 = str_sha256(\"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq\") ", + "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1" == util.hexstrdump(str_sha256("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"))); + result[6] = new test_result("SHA384 - cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7 = str_sha384(\"abc\") ", + "cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7" == util.hexstrdump(str_sha384("abc"))); + result[7] = new test_result("SHA384 - 3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b = str384(\"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq\") ", + "3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b" == util.hexstrdump(str_sha384("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"))); + result[8] = new test_result("SHA512 - ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f = str_sha512(\"abc\") ", + "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f" == util.hexstrdump(str_sha512("abc"))); + result[9] = new test_result("SHA512 - 204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445 = str_sha512(\"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq\") ", + "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445" == util.hexstrdump(str_sha512("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"))); + return result; +}); diff --git a/test/example-test.js b/test/example-test.js new file mode 100644 index 00000000..3bf3739e --- /dev/null +++ b/test/example-test.js @@ -0,0 +1,8 @@ + +unittests.register("Example test", function() { + var result = new Array(); + + result[0] = new test_result("test1 - testing on (\"str\" == \"str\")", ("str" == "str")); + result[1] = new test_result("test2 - testing on (1 == 1)", (1 == 1)); + return result; +}); diff --git a/test/index.html b/test/index.html new file mode 100644 index 00000000..6d9c3d1d --- /dev/null +++ b/test/index.html @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +OpenPGP.js testpage + + + + + +Unit Tests | Parser | Encryption / Decryption +

Print Debug Messages

+

Unit tests

+ +

+ + + +

Messages

+
+ + diff --git a/test/parser.html b/test/parser.html new file mode 100644 index 00000000..b2a862f1 --- /dev/null +++ b/test/parser.html @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +OpenPGP.js testpage + + + + + +Unit Tests | Parser | Encryption / Decryption +

Print Debug Messages

+

Parser

+OpenPGP Message | +OpenPGP Public Key | +OpenPGP Private Key
+ + +

Test output

+

+
+
+

Messages

+
+ + diff --git a/test/unittest.js b/test/unittest.js new file mode 100644 index 00000000..3a8e3b22 --- /dev/null +++ b/test/unittest.js @@ -0,0 +1,21 @@ + +var unittests = { + tests: Array(), + register: function(str_title, func_runtest) { + this.tests[this.tests.length] = {title: str_title, run: func_runtest}; + + }, + + runAll: function() { + var result = new Array(); + for (var i = 0; i < this.tests.length; i++) { + result[i] = {tests: this.tests[i].run(), title: this.tests[i].title }; + } + return result; + } +} + +function test_result(str_description, boolean_result) { + this.description = str_description; + this.result = boolean_result; +} \ No newline at end of file diff --git a/webmail/googlemail.com/gmail.js b/webmail/googlemail.com/gmail.js new file mode 100755 index 00000000..63d97d40 --- /dev/null +++ b/webmail/googlemail.com/gmail.js @@ -0,0 +1,70 @@ +// GPG4Browsers - An OpenPGP implementation in javascript +// Copyright (C) 2011 Recurity Labs GmbH +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library 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 +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +// The google mail interface +// these functions retrieve an email out of html +var Gmail = { + + getMail : function(callback) { + var unquote_printable = function(text) { + var result = ""; + while (text.indexOf("=") != -1) { + var i = text.indexOf("="); + result += text.substring(0, i); + result += String.fromCharCode(parseInt(text.substring(i+1, i+3),16)); + text = text.substring(i+3); + } + result += text; + return result; + }; + var xhr = new XMLHttpRequest(); + xhr.open("GET", "https://mail.google.com/mail/h/?v=om&th="+document.location.hash.split("/")[1],true); + xhr.onreadystatechange = function() { + if (xhr.readyState == 4) { + var mail = new Object(); + var msg = xhr.responseText.replace(/\r\n/g,"\n").replace(/\n/g,"\r\n"); + var header = msg.substring(0, msg.indexOf("\r\n\r\n")).replace(/\r\n /g," ").split("\r\n"); + mail.body = msg.substring(msg.indexOf("\r\n\r\n")+4); + for (var i = 0; i < header.length; i++) { + var split = header[i].split(": ") + if (split[0] == "Content-Transfer-Encoding" && split[1] == "quoted-printable") + mail.body = unquote_printable(mail.body); + } + mail.account = document.getElementsByTagName("title")[0].textContent.split(" - ")[2]; + + for (var i = 0; i < header.length; i++) { + var split = header[i].split(": "); + if (split.length < 2) continue; + if (split[0] == "To") + mail.to = split[1].split(", "); + else if (split[0] == "CC") + mail.cc = split[1].split(", "); + else if (split[0] == "Subject") + mail.subject = split[1]; + else if (split[0] == "From") + mail.from = split[1]; + else if (split[0] == "Date") + mail.date = new Date(split[1]); + } + callback(mail); + + } + }; + xhr.send(); +} +}; +