Starting Point

master
Zbigniew Diaczyszyn 2010-01-15 08:58:44 +01:00
parent 91106854a2
commit 0ac43216f0
40 changed files with 13472 additions and 0 deletions

BIN
gorilla-1.5alpha.tar.gz Normal file

Binary file not shown.

BIN
gorillademo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

113
sources/CHANGES.txt Executable file
View File

@ -0,0 +1,113 @@
- Password Gorilla 1.5 (29.11.2009)
- Add support for msgcat localisation
- Added German translation
- Added Themed Tk Widgets
- Package BWidget no longer included
- Password Gorilla 1.4
- Add support for Password Safe 3 file format.
- Add "Find".
- Hide main window when coming up and asking to open a file.
- Bugfix: fixed alphabetical ordering of entries in nested subgroups.
- Improved seed for initializing the pseudo-random number generator.
- Bring Password Gorilla window to the foreground after unlocking.
- Allow closing a locked database using the "X" button.
- Bugfix: fix handling of a database file name on the command line.
(Reported by Rudolf Mühlbauer.)
- Password Gorilla 1.3 (January 12, 2006)
- Bugfix: fix compatibility with Password Safe 2.14 and later.
(Fixes "V1 name field looks suspect" error.)
- Bugfix: disable the menu when locked (in idle timeout), which
on the Mac is not affected by a grab for all events by the
unlock dialog.
- Password Gorilla 1.2 (June 26, 2005)
- Added idle timeout (see Manage->Database Preferences).
- Added auto-save.
- Revised the exit dialog, if the database was modified. Instead of
asking whether to discard changes, it now asks whether to save the
modified database before exiting.
- Show percentage indicator when loading or saving a database.
- Added --rc <name> command-line option to use this configuration
file instead of ~/.gorillarc or the Registry.
- Bugfix: allow "\t" as a field separator for exporting.
- Bugfix: get confirmation when opening a new database, and the
current one is modified.
- Bugfix: on Windows, show the Windows (native) path name of the
database, i.e., using backslashes instead of slashes.
- Password Gorilla 1.1 (April 23, 2005)
- Added "Export" feature (in the "File" menu) to export the database
to a plain-text file (e.g., for backup purposes).
- Added "Backup database on save" preference. If enabled, a backup
copy will be created with the ".bak" extension.
- Consider password database changed if the master password has
changed -- and thus enable the "Save" menu item.
- Bugfix when using non-Latin character sets.
- For the Windows executable, add codeset pages for non-Latin
input.
- Added "-norc" command-line option. If used, default preferences
are used, and no preferences will be loaded or written to a
preferences file (Unix, Mac OS X) or the registry (Windows).
The option can also be made part of the executable name, e.g.,
"gorilla-norc.exe".
- Password Gorilla 1.0 (January 15, 2005)
- Added Password Gorilla artwork; contributed by Andrew J. Sniezek.
- Enabled mouse wheel on Windows.
- Unicode support: Encode all strings using UTF-8, so that a
database file may be moved between locales. If enabled, non-ASCII
characters may not show up correctly when loading the database
into Password Safe. If disabled, non-ASCII characters may not show
up correctly when opening the database in a different locale.
- Added dialog for database-specific preferences. So far, the only
configurable preference is "Unicode support".
- Merging a database now prompts whether the user wants to see a
detailed report of the merged logins (conflicts, added logins,
identical logins).
- Bug fix: "Edit->Copy Password" now indeed copies the password to
the clipboard, instead of the user name.
- Effect the "Remember sizes of dialog boxes" only if the version
number has not changed, as the size of dialog boxes may change
between versions.
- Password Gorilla 1.0b1 (December 17, 2004)
- Added "Credits" section to "Help->About" dialog.
- Fix problem on Windows, when the Page Up and Page Down keys would
scroll by two pages, when viewing the Help or License files.
- Assign and update "Created", "Last Modified" and "Password Last
Changed" timestamps; the latter two are shown in the Entry dialog.
- In the tree view, arrange all groups before the entries.
- Add "File->Preferences" dialog to configure, for now, the size
of the last recently used databases list, a delay for clearing
the clipboard, what action to take when double-clicking an entry,
and whether to save information about dialog box sizes.
- Provide some useful information in the bottom "status" line.
Clear the status line after 5 seconds, so that potentially
sensible information like "Password copied to clipboard" does
not show forever.
- Added option to clear the clipboard after a configurable amount
of time (see Preferences above), so that passwords don't remain
in the clipboard forever.
- Implemented "Merge" functionality, to merge entries from a
second database into the currently-open database. Duplicate
entries are ignored, if they are identical. Otherwise, they
will show up with the timestamp of their last modification.
- On the Macintosh, use the ~/Library/Preferences/gorilla.rc file
to store preferences.
- Rename "entry" to "login".
- Password Gorilla 1.0a2 (December 6, 2004)
- Repackaged as Starkit and Starpack, to offer a binary download
on Windows.
- Password Gorilla (December 5, 2004)
- Initial release.

339
sources/LICENSE.txt Executable file
View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
675 Mass Ave, Cambridge, MA 02139, USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
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 Program or any portion
of it, thus forming a work based on the Program, 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) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
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 Program, 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 Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) 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; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, 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 executable. However, as a
special exception, the source code 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.
If distribution of executable or 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 counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program 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.
5. 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 Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program 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 to
this License.
7. 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 Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program 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 Program.
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.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program 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.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies 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 Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, 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
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. 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 PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.

29
sources/blowfish/LICENSE.txt Executable file
View File

@ -0,0 +1,29 @@
Copyright (c) 2004, Frank Pilhofer
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
Neither the name of the package's author nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"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 COPYRIGHT
OWNER 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.

581
sources/blowfish/blowfish.tcl Executable file
View File

@ -0,0 +1,581 @@
#
# ----------------------------------------------------------------------
# Pure-Tcl implementation of the Blowfish algorithm.
# Written by Frank Pilhofer. Released under BSD license.
# ----------------------------------------------------------------------
#
# See below for more information on the interface.
#
# See http://www.schneier.com/blowfish.html for information about the
# Blowfish algorithm.
#
# The implementation is derived from Paul Kocher's implementation,
# available at http://www.schneier.com/blowfish-download.html
#
# ----------------------------------------------------------------------
#
package require Tcl 8.4
package require Itcl
catch {
itcl::delete class iblowfish::iblowfish
}
namespace eval iblowfish {}
itcl::class iblowfish::iblowfish {
private common ORIG_P {
0x243F6A88 0x85A308D3 0x13198A2E 0x03707344
0xA4093822 0x299F31D0 0x082EFA98 0xEC4E6C89
0x452821E6 0x38D01377 0xBE5466CF 0x34E90C6C
0xC0AC29B7 0xC97C50DD 0x3F84D5B5 0xB5470917
0x9216D5D9 0x8979FB1B
}
private common ORIG_S {
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
}
protected variable S
protected variable P
protected proc F {S x} {
set d [expr {$x & 0xff}]
set c [expr {($x >> 8) & 0xff}]
set b [expr {($x >> 16) & 0xff}]
set a [expr {($x >> 24) & 0xff}]
set S0a [lindex $S $a]
set S1b [lindex $S [expr {256 + $b}]]
set S2c [lindex $S [expr {512 + $c}]]
set S3d [lindex $S [expr {768 + $d}]]
set y [expr {($S0a + $S1b) & 0xffffffff}]
set y [expr {$y ^ $S2c}]
set y [expr {($y + $S3d) & 0xffffffff}]
return $y
}
protected proc intEncrypt {P S xl xr} {
for {set i 0} {$i < 16} {incr i} {
set Pi [lindex $P $i]
set xl [expr {$xl ^ $Pi}]
set f [F $S $xl]
set xr [expr {$f ^ $xr}]
set temp $xl
set xl $xr
set xr $temp
}
set temp $xl
set xl $xr
set xr $temp
set Pn [lindex $P 16]
set Pnpp [lindex $P 17]
set xr [expr {$xr ^ $Pn}]
set xl [expr {$xl ^ $Pnpp}]
return [list $xl $xr]
}
protected proc intDecrypt {P S xl xr} {
for {set i 17} {$i > 1} {incr i -1} {
set Pi [lindex $P $i]
set xl [expr {$xl ^$Pi}]
set f [F $S $xl]
set xr [expr {$f ^ $xr}]
set temp $xl
set xl $xr
set xr $temp
}
set temp $xl
set xl $xr
set xr $temp
set P1 [lindex $P 1]
set P0 [lindex $P 0]
set xr [expr {$xr ^ $P1}]
set xl [expr {$xl ^ $P0}]
return [list $xl $xr]
}
protected method init {key} {
set S $ORIG_S
set P [list]
set kl [string length $key]
binary scan $key c* kc
set j 0
for {set i 0} {$i < 18} {incr i} {
set data 0
for {set k 0} {$k < 4} {incr k} {
set kj [lindex $kc $j]
set kj [expr {($kj + 0x100) % 0x100}]
set data [expr {(($data << 8) | $kj) & 0xffffffff}]
if {[incr j] >= $kl} {
set j 0
}
}
set OPi [lindex $ORIG_P $i]
lappend P [expr {$OPi ^ $data}]
}
set datal 0
set datar 0
for {set i 0} {$i < 18} {incr i 2} {
set ed [intEncrypt $P $S $datal $datar]
set datal [lindex $ed 0]
set datar [lindex $ed 1]
set P [lreplace $P $i [expr {$i + 1}] $datal $datar]
}
for {set i 0} {$i < 4} {incr i} {
for {set j 0} {$j < 256} {incr j 2} {
set ed [intEncrypt $P $S $datal $datar]
set datal [lindex $ed 0]
set datar [lindex $ed 1]
set S [lreplace $S \
[expr {$i * 256 + $j}] \
[expr {$i * 256 + $j + 1}] \
$datal $datar]
}
}
}
#
# Initialize with key
#
constructor {key} {
init $key
}
}
#
# Blowfish - Electronic Codebook Mode.
#
# The encryptBlock and decryptBlock methods can be used to encrypt and
# decrypt a 64 bit (8 byte) block. Encryption and decryption is stateless.
#
catch {
itcl::delete class iblowfish::ecb
}
itcl::class iblowfish::ecb {
inherit iblowfish::iblowfish
constructor {key} {
iblowfish::iblowfish::constructor $key
} {
}
#
# Encrypt a 64 bit (8 octet) message block
#
public method encryptBlock {block} {
if {[binary scan $block II xl xr] != 2} {
error "block must be 8 bytes"
}
set xl [expr {($xl + 0x100000000) % 0x100000000}]
set xr [expr {($xr + 0x100000000) % 0x100000000}]
set d [intEncrypt $P $S $xl $xr]
return [binary format I2 $d]
}
#
# Decrypt a 64 bit (8 octet) message block
#
public method decryptBlock {block} {
if {[binary scan $block II xl xr] != 2} {
error "block must be 8 bytes"
}
set xl [expr {($xl + 0x100000000) % 0x100000000}]
set xr [expr {($xr + 0x100000000) % 0x100000000}]
set d [intDecrypt $P $S $xl $xr]
return [binary format I2 $d]
}
}
#
# Blowfish - Cipher Block Chaining
#
# Encrypt or decrypt a message. The object is initialized with the
# password and a salt (also known as the Initialization Vector). The
# salt must be exactly 8 bytes long; it is usually chosen at random.
# The encrypted message is of the same length as the cleartext message,
# but padded to the next multiple of 8 bytes.
#
# To decrypt a message, initialize the object with the same password
# and the same salt that were used for encryption. A decrypted message
# has the same (padded) length as the encrypted message. Protocols
# usually embed information about the message length, so that the
# decrypted message can be properly truncated to the length of the
# original cleartext message.
#
# The object can be used to encrypt/decrypt a stream, by calling the
# encrypt or decrypt method repeatedly, passing subsequent blocks of
# the stream.
#
# When encrypting a stream, all blocks but the last must be a multiple
# of 8 bytes in length.
#
# When decrypting a stream, all blocks, including the last, must be
# a multiple of 8 bytes in length. Again, truncation may be necessary
# if the original cleartext stream was not a multiple of 8 bytes in
# length.
#
# Note that the salt changes over time (i.e., it is updated after 8
# bytes each). To encrypt a new message with the same salt, or to
# switch between encryption and decryption, re-configure the "salt"
# variable, i.e.,
#
# set o [iblowfish::cbc #auto MyPassword 01234567]
# set cipher [$o encrypt "Hello World"]
# $o configure -salt 01234567
# set clear [$o decrypt $cipher] ;# Hello World (plus padding)
# itcl::delete object $o
#
catch {
itcl::delete class iblowfish::cbc
}
itcl::class iblowfish::cbc {
inherit iblowfish::iblowfish
public variable salt
constructor {key _salt} {
iblowfish::iblowfish::constructor $key
} {
set salt $_salt
if {[binary scan $salt II s0 s1] != 2} {
error "salt must be 8 bytes"
}
}
public method encrypt {message} {
if {[binary scan $salt II s0 s1] != 2} {
error "salt must be 8 bytes"
}
set s0 [expr {($s0 + 0x100000000) % 0x100000000}]
set s1 [expr {($s1 + 0x100000000) % 0x100000000}]
set mlen [string length $message]
if {($mlen % 8) != 0} {
set padLen [expr {7-($mlen%8)}]
append message [string range "\x00\x00\x00\x00\x00\x00\x00" 0 $padLen]
incr mlen $padLen
incr mlen
}
set result ""
for {set i 0} {$i < $mlen} {incr i 8} {
if {[binary scan $message @[set i]II xl xr] != 2} {
error "oops"
}
set xl [expr {($xl + 0x100000000) % 0x100000000}]
set xr [expr {($xr + 0x100000000) % 0x100000000}]
set xl [expr {$xl ^ $s0}]
set xr [expr {$xr ^ $s1}]
set d [intEncrypt $P $S $xl $xr]
set s0 [lindex $d 0]
set s1 [lindex $d 1]
append result [binary format I2 $d]
}
set salt [binary format II $s0 $s1]
return $result
}
public method decrypt {message} {
if {[binary scan $salt II s0 s1] != 2} {
error "salt must be 8 bytes"
}
set s0 [expr {($s0 + 0x100000000) % 0x100000000}]
set s1 [expr {($s1 + 0x100000000) % 0x100000000}]
set mlen [string length $message]
if {($mlen % 8) != 0} {
error "message must be a multiple of 8 bytes"
}
set result ""
for {set i 0} {$i < $mlen} {incr i 8} {
if {[binary scan $message @[set i]II xl xr] != 2} {
error "oops"
}
set xl [expr {($xl + 0x100000000) % 0x100000000}]
set xr [expr {($xr + 0x100000000) % 0x100000000}]
set d [intDecrypt $P $S $xl $xr]
set d0 [lindex $d 0]
set d1 [lindex $d 1]
set c0 [expr {$d0 ^ $s0}]
set c1 [expr {$d1 ^ $s1}]
set s0 $xl
set s1 $xr
append result [binary format II $c0 $c1]
}
set salt [binary format II $s0 $s1]
return $result
}
}
package provide iblowfish 0.2

145
sources/blowfish/blowtest.tcl Executable file
View File

@ -0,0 +1,145 @@
#
# Test vectors by Eric Young
# http://www.schneier.com/code/vectors.txt
#
package require Itcl
package require Tcl 8.4
package require iblowfish
proc h2b {hex} {
return [binary format H* $hex]
}
proc b2h {bin} {
binary scan $bin H* dummy
return $dummy
}
#
# ecb test data; key bytes, clear bytes, cipher bytes
#
set testVectors {
0000000000000000 0000000000000000 4EF997456198DD78
FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF 51866FD5B85ECB8A
3000000000000000 1000000000000001 7D856F9A613063F2
1111111111111111 1111111111111111 2466DD878B963C9D
0123456789ABCDEF 1111111111111111 61F9C3802281B096
1111111111111111 0123456789ABCDEF 7D0CC630AFDA1EC7
0000000000000000 0000000000000000 4EF997456198DD78
FEDCBA9876543210 0123456789ABCDEF 0ACEAB0FC6A0A28D
7CA110454A1A6E57 01A1D6D039776742 59C68245EB05282B
0131D9619DC1376E 5CD54CA83DEF57DA B1B8CC0B250F09A0
07A1133E4A0B2686 0248D43806F67172 1730E5778BEA1DA4
3849674C2602319E 51454B582DDF440A A25E7856CF2651EB
04B915BA43FEB5B6 42FD443059577FA2 353882B109CE8F1A
0113B970FD34F2CE 059B5E0851CF143A 48F4D0884C379918
0170F175468FB5E6 0756D8E0774761D2 432193B78951FC98
43297FAD38E373FE 762514B829BF486A 13F04154D69D1AE5
07A7137045DA2A16 3BDD119049372802 2EEDDA93FFD39C79
04689104C2FD3B2F 26955F6835AF609A D887E0393C2DA6E3
37D06BB516CB7546 164D5E404F275232 5F99D04F5B163969
1F08260D1AC2465E 6B056E18759F5CCA 4A057A3B24D3977B
584023641ABA6176 004BD6EF09176062 452031C1E4FADA8E
025816164629B007 480D39006EE762F2 7555AE39F59B87BD
49793EBC79B3258F 437540C8698F3CFA 53C55F9CB49FC019
4FB05E1515AB73A7 072D43A077075292 7A8E7BFA937E89A3
49E95D6D4CA229BF 02FE55778117F12A CF9C5D7A4986ADB5
018310DC409B26D6 1D9D5C5018F728C2 D1ABB290658BC778
1C587F1C13924FEF 305532286D6F295A 55CB3774D13EF201
0101010101010101 0123456789ABCDEF FA34EC4847B268B2
1F1F1F1F0E0E0E0E 0123456789ABCDEF A790795108EA3CAE
E0FEE0FEF1FEF1FE 0123456789ABCDEF C39E072D9FAC631D
0000000000000000 FFFFFFFFFFFFFFFF 014933E0CDAFF6E4
FFFFFFFFFFFFFFFF 0000000000000000 F21E9A77B71C49BC
0123456789ABCDEF 0000000000000000 245946885754369A
FEDCBA9876543210 FFFFFFFFFFFFFFFF 6B5C5A9C5D9E0A5A
}
set testNum 0
set passed 0
set failed 0
foreach {key clear cipher} $testVectors {
incr testNum
set engine [iblowfish::ecb \#auto [h2b $key]]
set encrypted [$engine encryptBlock [h2b $clear]]
set decrypted [$engine decryptBlock $encrypted]
itcl::delete object $engine
if {![string equal -nocase $cipher [b2h $encrypted]]} {
puts "varkey-$testNum: encryption failed: [b2h $encrypted] != $cipher"
incr failed
} elseif {![string equal -nocase $clear [b2h $decrypted]]} {
puts "varkey-$testNum: decryption failed: [b2h $decrypted] != $clear"
incr failed
} else {
puts "varkey-$testNum: passed"
incr passed
}
}
#
# set key tests; fixed cleartext, variable key length
#
set key [h2b "F0E1D2C3B4A5968778695A4B3C2D1E0F0011223344556677"]
set clear [h2b "FEDCBA9876543210"]
set cipher {
F9AD597C49DB005E E91D21C1D961A6D6 E9C2B70A1BC65CF3 BE1E639408640F05
B39E44481BDB1E6E 9457AA83B1928C0D 8BB77032F960629D E87A244E2CC85E82
15750E7A4F4EC577 122BA70B3AB64AE0 3A833C9AFFC537F6 9409DA87A90F6BF2
884F80625060B8B4 1F85031C19E11968 79D9373A714CA34F 93142887EE3BE15C
03429E838CE2D14B A4299E27469FF67B AFD5AED1C1BC96A8 10851C0E3858DA9F
E6F51ED79B9DB21F 64A6E14AFD36B46F 80C7D7D45A5479AD 05044B62FA52D080
}
set testNum 0
for {set i 0} {$i < [string length $key]} {incr i} {
incr testNum
set engine [iblowfish::ecb \#auto [string range $key 0 $i]]
set encrypted [$engine encryptBlock $clear]
set decrypted [$engine decryptBlock $encrypted]
itcl::delete object $engine
if {![string equal -nocase [lindex $cipher $i] [b2h $encrypted]]} {
puts "setkey-$testNum: encryption failed: [b2h $encrypted] != [lindex $cipher $i]"
incr failed
} elseif {![string equal -nocase [b2h $clear] [b2h $decrypted]]} {
puts "setkey-$testNum: decryption failed: [b2h $decrypted] != [b2h $clear]"
incr failed
} else {
puts "setkey-$testNum: passed"
incr passed
}
}
#
# chaining mode test data
#
set key [h2b "0123456789ABCDEFF0E1D2C3B4A59687"]
set iv [h2b "FEDCBA9876543210"]
set clear [h2b "37363534333231204E6F77206973207468652074696D6520666F722000"]
set cbccipher [h2b "6B77B4D63006DEE605B156E27403979358DEB9E7154616D959F1652BD5FF92CC"]
set clearlen [string length $clear]
set testNum 0
set engine [iblowfish::cbc \#auto $key $iv]
set encrypted [$engine encrypt $clear]
$engine configure -salt $iv
set decrypted [$engine decrypt $encrypted]
set decrypted [string range $decrypted 0 [expr {$clearlen - 1}]]
itcl::delete object $engine
if {![string equal -nocase [b2h $cbccipher] [b2h $encrypted]]} {
puts "chaining-$testNum: encryption failed: [b2h $encrypted] != [b2h $cbccipher]"
incr failed
} elseif {![string equal -nocase [b2h $clear] [b2h $decrypted]]} {
puts "chaining-$testNum: decryption failed: [b2h $decrypted] != [b2h $clear]"
incr failed
} else {
puts "chaining-$testNum: passed"
incr passed
}
puts "$passed tests passed, $failed tests failed."

3
sources/blowfish/pkgIndex.tcl Executable file
View File

@ -0,0 +1,3 @@
package ifneeded iblowfish 0.2 [list source [file join $dir blowfish.tcl]]

6668
sources/gorilla15alpha.tcl Executable file

File diff suppressed because it is too large Load Diff

482
sources/help.txt Executable file
View File

@ -0,0 +1,482 @@
_________________________________________________________________
Using Password Gorilla
Table of Contents
* Help Using Password Gorilla
+ Starting
+ Choosing a Master Password
+ Organization Into Groups
+ Logins
+ Preferences
+ Information about the "V3" Database Format
* Risks
+ The Software Itself
+ System Failure
+ Other Users on a Shared Computer
+ Your System Administrator
+ Viruses, Backdoors, etc.
+ Putting Risk In Perspective
_________________________________________________________________
Help Using Password Gorilla
_________________________________________________________________
Starting
Upon start-up, Password Gorilla shows the "Open Password Database"
dialog. It shows a list of recently used password database files,
allows you to browse for a different password database file, and asks
for the database's master password. Once a file is chosen, and the
master password is entered correctly, the password database is opened,
and its contents are shown.
To create a new, empty password database, click the "Cancel" button,
and then select "New" from the "File" menu. You will be asked for an
initial master password for the new password database.
Choosing a Master Password
It goes without saying that the master password should be non-trivial.
I.e., the master password should not be a word in any language nor a
name. Such trivial passwords are subject to dictionary attacks, in
which an attacker could gain access to your master password by simply
using "brute force," by trying all the words in the dictionary.
Equally important is that the master password should not be kept in
the same place as the password database. Ideally, the master password
should not be written down at all, so that it remains in your personal
memory only. If you decide to write down your master password, keep it
away from your computer(s), in a location that only you know of.
Because the password database is encrypted using your master password
(using the peer-reviewed and commercially well-accpted Twofish
algorithm, for the technically inclined), it is imperative that you do
not forget your master password. It is impossible to recover a lost
master password. An encrypted password database can not be "cracked,"
as long as the master password is not trivial (see above).
Note that Password Gorilla does not try to second-guess your choice of
password. It does not check for or complain about passwords that would
generally be considered weak.
Organization Into Groups
Logins in the password database are shown as a tree, organized into
nested groups. Groups allow you to arrange logins by category. Click
on the plus sign next to a group's name in order to view its subgroups
and logins that the group contains.
New groups and subgroups can be added by right-clicking on a group
name, and choosing the "Add Subgroup" option. (Macintosh users, hold
the Control key, and click on a group name.) In the dialog box that
opens, the "Parent Group" name will be set automatically, and the name
of the new subgroup can be entered.
You can also drag both groups and logins, and drop them on a new
parent group, to quickly re-arrange the database's organization.
Note that empty groups are not stored in the password database file.
If you save and re-open a file, empty groups will disappear.
Logins
Groups can contain any number of logins. To add a new login,
right-click on a group, and choose the "Add Login" option. To edit an
existing login, right-click on an login, and select the "Edit Login"
option. (Macintosh users, hold down the Control key, and click on a
group name.)
The following information is managed for each login:
Group
The name of the group that this login is in. The names of
hierarchical groups are concatenated, separated by a dot. A
login can be moved to a different group by editing this field
-- though dragging the login and dropping it on its new group
is more convenient.
Title
The login's title is shown in the main window, so that you can
identify the service that this login information belongs to,
e.g., "E-Mail."
URL (V3 format only.)
The service's URL, if any. In the tree view, you can
right-click on a login and choose "Copy URL to Clipboard" in
order to copy this data to the clipboard, for pasting it into
your browser's address bar. This field is only available when
using the "V3" database format; see the discussion of the
V3 format below for more information.
Username
Your username for this service. In the tree view, you can
right-click on a login and choose "Copy Username To Clipboard"
in order to copy this data to the clipboard, for pasting it
into the service's login prompt.
Password
The password that is associated with this login. In the tree
view, you can right-click on a login and choose "Copy Password
To Clipboard" in order to paste it into the service's login
prompt.
Notes
You can use the notes field for arbitrary information that you
wish to associate with the login. E.g., you could note
questions and answers to a service's security questions (of the
"What is your mother's maiden name?" kind). Also, you can use
the field for the service's URL. If the notes include a string
that starts with "http" or "https", or if they contain the
token "url:" followed by a URL (put the URL in quotes, if it
contains spaces), then you can right-click on an login and
choose "Copy URL To Clipboard" in order to paste the URL into
your Web browser.
When editing a login, the password is not shown, for added protection
from curious onlookers. You can click the "Show Password" button to
toggle visibility of the password.
Clicking on "Generate Password" generates a new pseudo-random password
according to the current password policy (which is a per-database
setting that can be set using the "Password Policy" option in the
"Manage" menu). If the "Override Password Policy" box is checked, you
can edit the password policy to use for this one password.
The password policy allows you to set the length of randomly generated
passwords, and the characters to use. Check the "use easy to read
characters only" option to avoid characters that look similar. This
excludes the lower- and uppercase letters 'i', 'j', 'l' and 'o', the
digits '0' and '1', and the exclamation mark, pipe symbol, and
parentheses. Note: to generate random hexadecimal passwords, the "use
hexadecimal digits" option should be checked exclusively, and the
password length should be an even number.
The default password policy for the current database can be set using
the "Password Policy" dialog from the "Manage" menu.
Preferences
General, database default and export preferences can be configured
using the "Preferences" dialog from the "File" menu. Database
preferences, which are specific to the current password database, can
be configured using "Database Preferences" from the "Manage" menu,
when a database is open.
General Preferences
Clear clipboard after <nn> seconds
If this value is set to a non-zero value, the system clipboard
is cleared, the specified number of seconds after copying a
user name, password or URL to the clipboard. This ensures that
no password remains in the clipboard forever.
Remember <nn> database names
Makes Password Gorilla only remember the given number of most
recently used database file names. If set to zero, Password
Gorilla will not remember database file names at all. The
current list of file names in the most recently used list can
be cleared with the button next to this option.
When double clicking a login
Configures what to do when you double-click on a login. The
options are to copy the login's password to the clipboard, to
open the login for editing, or to do neither.
Backup database on save
If enabled, then a backup file of the password database is made
whenever the database is saved. The backup file has the same
name as the database itself, but with the ".bak" extension.
Remember sizes of dialog boxes
Password Gorilla allows you to resize its main window, and most
of its dialogs, should you find the default sizes inconvenient.
If this option is enabled, Password Gorilla will remember the
size of dialog boxes across restarts.
Database Default Preferences
Database default preferences are applied to new databases, but do not
affect existing databases. To change a setting for an existing
database, go to "Database Preferences" in the "Manage" menu. See the
discussion of Database Preferences below.
Export Preferences
Export preferences apply to the exporting of a password database to a
plain-text file.
Include password field
If enabled, the password is included in the exported file. If
disabled, "********" is substituted for the password.
Include "Notes" field
If enabled, the "Notes" are included in the exported file.
Field separator
Configures the character to separate fields. This character
should not appear in any user name or password. Common
separators are ":" (colon) and "," (comma).
Show security warning
If enabled, reminds the user, before exporting the database,
that the exported plain-text file is not encrypted or password
protected.
Database Preferences
These database preferences can be configured using "Database
Preferences" from the "Manage" menu.
Lock when idle after <nn> minutes
If this preference is set to a non-zero value, Password Gorilla
will lock the database after a period of inactivity. In that
case, a dialog box opens, prompting for the database's master
password. The dialog also allows to exit Password Gorilla. Note
that this allows a malicious user to exit the application,
discarding changes. However, this is not a security issue: a
malicious user, having access to your desktop, could just as
well kill the application (e.g., from the console or the the
Task Manager). A better choice is to lock your desktop while
unattended.
Auto-save database immediately when changed
If enabled, then the database will automatically be saved after
each change.
Use Password Safe 3 format
If this option is enabled (i.e., checked), the password
database, when saved, will use the new "Password Safe 3"
encryption format. The database will be compatible with
Password Safe 3, but will not be compatible with versions of
Password Safe prior to 3.0, or versions of Password Gorilla
prior to 1.4. If this option is disabled (i.e., not checked),
the old Password Safe 2 database format is used: the database
will be compatible with Password Safe 2.0 or higher, and
Password Gorilla 1.0 or higher.
It is highly recommended to enable this option, and to upgrade
existing databases to the Password Safe 3 format, which
features enhanced security. See below, Information about
the new Database Format, for more information.
V2 Unicode support
Whether the database file uses Unicode. This is the default for
the Password Safe 3 format; this option only applies if the
"Use Password Safe 3 format" option is disabled (i.e., not
checked). If this option is enabled, database files containing
international characters can safely be exchanged across
locales, e.g., when you want to use the same password database
in both a Western European and Russian locale. The caveat is
that, if you use both Password Gorilla and Password Safe, the
latter will not read accented characters correctly. (The
database will open, but non-ASCII characters will not show up
correctly.) This option has no effect if your database uses
only ASCII characters.
V3 key stretching iterations
The Password Safe 3 format supports "variable key stretching",
which is a means of protecting a database against brute-force
attacks. Key stretching is a complex operation that must be
performed when validating a master password for correctness.
When an authorized user enters the correct password, this may
take a second and is barely noticeable. But it slows down
mass-testing password guesses by an automated program. The
"iterations" parameter indicates the complexity of the key
stretching: the higher this value, the longer it takes to open
a database, and the longer it takes an attacker to test one
password. It is recommended to leave the value at its default
of 2048, which is a good compromise between promptly opening
databases and hampering attackers.
Note that this option can only be changed on a per-database
basis and does not appear in the Database Default Preferences
Menu. This prevents a malicious user from changing the
preference in the registry and degrading the protection of
future databases.
Information about the new Database Format
"V3" Format Introduction
Password Gorilla 1.4 adds support for a new encrypted format for
password databases, as introduced by version 3 of Password Safe --
therefore also called the "V3" format. This new format is based on the
years of experience with and analysis of the prior "V2" database
format, and features enhanced security (see below, V2 Format
Weakness for details). The new format offers:
* Support for non-ASCII character sets by default.
* A stand-alone field for a URL.
* A checksum to detect tampering or truncation.
* Use of the improved Twofish encryption algorithm.
* Stronger protection against brute-force attacks on the master
password.
It is recommended to use the new format, and to upgrade existing
password databases, unless you require compatibility with software
that supports the old format only, such as Password Safe 2.x, or
versions of Password Gorilla prior to 1.4.
Switching between the V2 and V3 Formats
Password Gorilla defaults to the V3 format for newly created
databases, but it does not automatically upgrade existing V2
databases. Upgrading, as well as downgrading, a database is
accomplished by enabling the Use Password Safe 3 format checkbox in
the Database Preferences menu (see above).
Password Safe recommends to use the ".psafe3" extension for V3-format
database files, and the ".dat" extension for V2-format files.
(Password Gorilla allows password database files in either format to
have any extension.)
V2 Format Weakness
In the interest of full disclosure, it should be noted that a
potential weakness was discovered with the old Password Safe 2 ("V2")
file format. This issue affected the "key stretching" process that is
intended to slow down a brute force attack against a database's master
password (i.e., repeated attempts at guessing the password). The
weakness in the file format's design allowed brute force attacks 1000
times faster than intended. The number sounds worse than it is: a
good, long master password is one among billions of billions of
combinations, and a factor of 1000 does not make a practical
difference. However, the factor may have an impact on the security of
password databases that use a short, more easily guessable master
password. The Password Safe 3 format avoids this issue by depending on
the result of the key stretching operation (which is computationally
expensive) as an input to decryption -- therefore, the operation can
not be bypassed.
_________________________________________________________________
Risks
_________________________________________________________________
Just like re-using the same passwords over and over again, or keeping
passwords written down on a sticker glued to the bottom of your desk,
some risks are associated with the use of Password Gorilla. This
section is not meant to scare you, but as an educated user, you have
the right to know about potential risks, and to make informed
decisions. Risks should not be ignored, but evaluated and addressed.
There are different threat vectors that can be considered:
The Software Itself
First of all, the software itself may be a risk. For all you know, the
software's author could be a sociopath who tries to talk you into
downloading and installing buggy software that secretly broadcasts
your passwords. If you want, you can trust the author, third-party
recommendations, you can inspect the source code for trap doors, or
trust a third-party code inspection.
Maybe the software is not bug-free. In an extreme scenario, a bug in
Password Gorilla could destroy your password database. It is good
advice to keep a backup copy of your password database in a safe
place.
System Failure
Sometimes, computers have the annoying habit to crash at the most
unfortunate time. Many users have lost data due to an unpredicted
crash. This can be problematic, e.g., when you just added or modified
a login, and did not get around to saving the updated password
database. If a password was randomly generated, it may be lost.
The easy workaround is to not confirm your password with the online
service before saving the password database. I.e., when creating a new
login, first add it in Password Gorilla, and immediately save the
database (using "Save" from the "File" menu). Only then go to your
online service -- e.g., the Web site that required registration, and
complete its signup process. When modifying a login, e.g., changing
the password, there is a chance that the computer might crash after
saving the database, but before completing the service's password
change process. In this case, the old password will still be available
in the password database's backup file -- assuming that you have a
backup copy, of course.
Other Users on a Shared Computer
Common sense will go a long way in protecting your password database
from other users that you share a computer with. Never keep Password
Gorilla running when you leave the desktop unlocked. Make sure that
the database file is not readable by other users -- while the database
format is considered secure, this prevents other users from copying
the file, and making a brute-force attempt of guessing the master
password offline.
If you follow these precautions, there is nothing to worry about here.
Your System Administrator
If your computer is administered by somebody else than you, then you
need to trust the administrator(s). An administrator can bypass the
operating system's security measures, and inspect a running program's
in-memory data. Password Gorilla obviously needs to have the decrypted
contents of your password database in memory, so a malicious
administrator could access Password Gorilla's memory, and gain access
to your passwords. In an attempt to foil naive attackers, Password
Gorilla takes some care by not storing data in clear text, but
encrypted using a temporary key. However, because the key is also kept
in memory by necessity, a motivated attacker could find both the
encrypted password and its key.
Of course, malicious administrators have a wide range of tools at
their disposal that invade on your privacy, in order to gain access to
your passwords. An administrator could replace the Password Gorilla
software with a trojan version that looks and acts the same, but sends
your passwords to the administrator's account. Even if you are not
using Password Gorilla, the administrator could install a key logger,
or monitor your internet connection, to find passwords as you type
them.
The added risk of using Password Gorilla is that a malicious
administrator could compromise all passwords at once, instead of only
intercepting the few passwords that you actually use and transmit in
one session.
Viruses, Backdoors, etc.
If your computer is infected by spyware or viruses, then external
malicious users may have control over your computer. Such users could
use the same attacks as described for a system administrator above. It
is a good idea to check for viruses and spyware on a regular basis.
Password Gorilla should obviously not be used on a compromised
computer.
Putting Risk In Perspective
The above does not necessarily imply that Password Gorilla is too
unsecure to use. They are merely a set of risks that need to be
considered and evaluated in order to make an informed decision, and to
take some common sense precautions. The author believes that using
Password Gorilla is a better idea than the alternative of writing down
passwords, or of reusing passwords.
As the example of the malicious system administrator shows, there is a
wide range of attacks that are possible even if you were not using
Password Gorilla.
Saying that Password Gorilla should not be used on a computer that is
infected with spyware, viruses, or backdoors, is good advice, but
redundant, as the problem is not limited to Password Gorilla. A
compromised computer should not be used for anything, especially not
for private communication.
Also, while technical attacks receive a lot of publicity, it should
not be forgotten that social engineering attacks are usually more
effective. In a study that I read about, a sizeable fraction of users
revealed their passwords to strangers on the street, for a mere piece
of chocolate. In one of the Hollywood movies that treat this subject
better than others, War Games, the protagonist gains access not by
pressing a magic button, or by bypassing security, but by spending
countless hours trying to get into the designer's mind, in a social
engineering attack to guess the designer's most likely choice of
password -- another reason to prefer random passwords.
_________________________________________________________________
Frank Pilhofer, fp@fpx.de
Last modified: Sat Jun 12 22:30:00 2006

289
sources/isaac.tcl Executable file
View File

@ -0,0 +1,289 @@
#
# ----------------------------------------------------------------------
# ISAAC: a fast cryptographic random number generator
# Derived from the source code for ISAAC by Bob Jenkins.
#
# ISAAC (Indirection, Shift, Accumulate, Add, and Count) generates
# 32-bit random numbers. Cycles are guaranteed to be at least 2^40
# values long, and they are 2^8295 values long on average. The
# results are uniformly distributed, unbiased, and unpredictable
# unless you know the seed.
#
# For more information, see the ISAAC homepage at
# http://www.burtleburtle.net/bob/rand/isaacafa.html
#
# This implementation (c) 2004 by Frank Pilhofer. Released under BSD
# license.
# ----------------------------------------------------------------------
#
package require Tcl 8.4
namespace eval isaac {
#
# Random numbers
#
variable randrsl
variable randcnt 256
#
# Internal state
#
variable mm
variable aa
variable bb
variable cc
}
#
# Mix helper
#
proc isaac::mix {a b c d e f g h} {
set a [expr {($a ^ ($b << 11)) & 0xffffffff}]
set d [expr {($d + $a) & 0xffffffff}]
set b [expr {($b + $c) & 0xffffffff}]
set b [expr {($b ^ ($c >> 2)) & 0xffffffff}]
set e [expr {($e + $b) & 0xffffffff}]
set c [expr {($c + $d) & 0xffffffff}]
set c [expr {($c ^ ($d << 8)) & 0xffffffff}]
set f [expr {($f + $c) & 0xffffffff}]
set d [expr {($d + $e) & 0xffffffff}]
set d [expr {($d ^ ($e >> 16)) & 0xffffffff}]
set g [expr {($g + $d) & 0xffffffff}]
set e [expr {($e + $f) & 0xffffffff}]
set e [expr {($e ^ ($f << 10)) & 0xffffffff}]
set h [expr {($h + $e) & 0xffffffff}]
set f [expr {($f + $g) & 0xffffffff}]
set f [expr {($f ^ ($g >> 4)) & 0xffffffff}]
set a [expr {($a + $f) & 0xffffffff}]
set g [expr {($g + $h) & 0xffffffff}]
set g [expr {($g ^ ($h << 8)) & 0xffffffff}]
set b [expr {($b + $g) & 0xffffffff}]
set h [expr {($h + $a) & 0xffffffff}]
set h [expr {($h ^ ($a >> 9)) & 0xffffffff}]
set c [expr {($c + $h) & 0xffffffff}]
set a [expr {($a + $b) & 0xffffffff}]
return [list $a $b $c $d $e $f $g $h]
}
#
# Initialize, from a (binary) seed string
#
proc isaac::init {seed} {
variable aa
variable bb
variable cc
variable mm
#
# Seed needs to be 256 * 32 bit integers
#
set slen [string length $seed]
if {$slen < 1024} {
append seed [string repeat "\0" [expr {1024-$slen}]]
}
binary scan $seed i256 iseed
#
# Initialize
#
set aa 0
set bb 0
set cc 0
set mm [list]
set a 0x9e3779b9
set b 0x9e3779b9
set c 0x9e3779b9
set d 0x9e3779b9
set e 0x9e3779b9
set f 0x9e3779b9
set g 0x9e3779b9
set h 0x9e3779b9
set tmm [list]
for {set i 0} {$i < 4} {incr i} {
foreach {a b c d e f g h} [mix $a $b $c $d $e $f $g $h] {}
}
for {set i 0} {$i < 256} {incr i 8} {
incr a [lindex $iseed $i]
incr b [lindex $iseed [expr {$i + 1}]]
incr c [lindex $iseed [expr {$i + 2}]]
incr d [lindex $iseed [expr {$i + 3}]]
incr e [lindex $iseed [expr {$i + 4}]]
incr f [lindex $iseed [expr {$i + 5}]]
incr g [lindex $iseed [expr {$i + 6}]]
incr h [lindex $iseed [expr {$i + 7}]]
set a [expr {$a & 0xffffffff}]
set b [expr {$b & 0xffffffff}]
set c [expr {$c & 0xffffffff}]
set d [expr {$d & 0xffffffff}]
set e [expr {$e & 0xffffffff}]
set f [expr {$f & 0xffffffff}]
set g [expr {$g & 0xffffffff}]
set h [expr {$h & 0xffffffff}]
foreach {a b c d e f g h} [mix $a $b $c $d $e $f $g $h] {}
lappend tmm $a $b $c $d $e $f $g $h
}
for {set i 0} {$i < 256} {incr i 8} {
incr a [lindex $tmm $i]
incr b [lindex $tmm [expr {$i + 1}]]
incr c [lindex $tmm [expr {$i + 2}]]
incr d [lindex $tmm [expr {$i + 3}]]
incr e [lindex $tmm [expr {$i + 4}]]
incr f [lindex $tmm [expr {$i + 5}]]
incr g [lindex $tmm [expr {$i + 6}]]
incr h [lindex $tmm [expr {$i + 7}]]
set a [expr {$a & 0xffffffff}]
set b [expr {$b & 0xffffffff}]
set c [expr {$c & 0xffffffff}]
set d [expr {$d & 0xffffffff}]
set e [expr {$e & 0xffffffff}]
set f [expr {$f & 0xffffffff}]
set g [expr {$g & 0xffffffff}]
set h [expr {$h & 0xffffffff}]
foreach {a b c d e f g h} [mix $a $b $c $d $e $f $g $h] {}
lappend mm $a $b $c $d $e $f $g $h
}
isaac
}
#
# Produce some more random numbers
#
proc isaac::isaac {} {
variable aa
variable bb
variable cc
variable mm
variable randrsl
variable randcnt
set cc [expr {($cc + 1) & 0xffffffff}]
set bb [expr {($bb + $cc) & 0xffffffff}]
set randrsl [list]
for {set i 0} {$i < 256} {incr i} {
set x [lindex $mm $i]
if {($i % 4) == 0} {
set aa [expr {($aa ^ ($aa << 13)) & 0xffffffff}]
} elseif {($i % 4) == 1} {
set aa [expr {($aa ^ ($aa >> 6)) & 0xffffffff}]
} elseif {($i % 4) == 2} {
set aa [expr {($aa ^ ($aa << 2)) & 0xffffffff}]
} else {
set aa [expr {($aa ^ ($aa >> 16)) & 0xffffffff}]
}
set tmp [lindex $mm [expr {($i + 128) & 0xff}]]
set aa [expr {($tmp + $aa) & 0xffffffff}]
set tmp [lindex $mm [expr {($x >> 2) & 0xff}]]
set y [expr {($tmp + $aa + $bb) & 0xffffffff}]
set mm [lreplace $mm $i $i $y]
set tmp [lindex $mm [expr {($y >> 10) & 0xff}]]
set bb [expr {($tmp + $x) & 0xffffffff}]
lappend randrsl $bb
}
set randcnt 0
}
#
# ----------------------------------------------------------------------
# Public interface
# ----------------------------------------------------------------------
#
#
# Initialize with a random (binary string) seed
#
proc isaac::srand {seed} {
init $seed
}
#
# Generates an integer random number in the [0,0xffffffff] interval
#
proc isaac::int32 {} {
variable randcnt
variable randrsl
if {$randcnt >= 256} {
isaac
}
set res [lindex $randrsl $randcnt]
incr randcnt
return $res
}
#
# Generates a floating-point random number in the [0,1) interval
#
proc isaac::rand {} {
set tmp [int32]
return [expr {double($tmp) / 4294967296.0}]
}
#
# ----------------------------------------------------------------------
# Print test vectors, for comparison with the original code
# ----------------------------------------------------------------------
#
proc isaac::test {} {
variable randrsl
init [string repeat "\0" 1024]
for {set i 0} {$i < 2} {incr i} {
isaac
for {set j 0} {$j < 256} {incr j} {
puts -nonewline [format "%.8x " [lindex $randrsl $j]]
if {($j & 7) == 7} {
puts ""
}
}
}
}
proc isaac::test2 {} {
srand [string repeat "\0" 1024]
for {set j 0} {$j < 256} {incr j} {
int32
}
for {set i 0} {$i < 2} {incr i} {
for {set j 0} {$j < 256} {incr j} {
set random [int32]
puts -nonewline [format "%.8x " $random]
if {($j & 7) == 7} {
puts ""
}
}
}
}

195
sources/msgs/de.msg Normal file
View File

@ -0,0 +1,195 @@
;# Datenbank für die deutsche Übersetzung
mcmset de {
"File" "Datenbank" \
"New ..." "Neue ..."\
"Open ..." "Öffnen ..." \
"Merge ..." "Importieren ..."\
"Save" "Speichern"\
"Add" "neu" \
"Save As ..." "Speichern als ..." \
"Export ..." "Exportieren ..." \
"Preferences ..." "Einstellungen ..." \
"Edit" "Bearbeiten" \
"Copy Username" "Benutzernamen kopieren" \
"Copy Password" "Passwort kopieren" \
"Copy URL" "URL kopieren" \
"Clear Clipboard" "Zwischenablage löschen" \
"Find ..." "Suchen ..." \
"Find next" "Nächstes" \
"Manage" "Verwalten" \
"Help ..." "Hilfe ..." \
"Help" "Hilfe" \
"License ..." "Lizenz ..." \
"About ..." "Über ..."
"Open Password Database" "Passwort-Datenbank öffnen" \
"Select a database, and enter its password." "Wähle eine Datenbank und gib das Passwort ein" \
"Create new password database" "Neue Passwort-Datenbank anlegen" \
"Open password database" "Passwort-Datenbank öffnen" \
"Merge a second database into this database" "Importiere eine zweite Datenbank" \
"Add a new Group" "Neue Gruppe hinzufügen" \
"Add Subgroup ..." "Untergruppe hinzufügen ..." \
"Add Group ..." "Gruppe hinzufügen" \
"Delete Group" "Gruppe löschen" \
"Rename Group ..." "Gruppe umbenennen" \
"Parent Group" "Übergeordnete Gruppe" \
"Group Name" "Gruppename" \
"Notes:" "Notizen" \
"Show Password" "Passwort zeigen" \
"Hide Password" "Passwort verbergen" \
"Password Policy ..." "Passwort Richtlinien" \
"Database Preferences" "Datenbank Einstellungen" \
"Database Preferences ..." "Datenbank Einstellungen ..." \
"Generate Password" "Passwort erzeugen" \
"Change Master Password ..." "Master Passwort ändern ..." \
"Override Password Policy" "Passwort-Richtlinien\nnicht beachten" \
"Password Length" "Passwortlänge" \
"Use lowercase letters" "Kleinbuchstaben verwenden" \
"Use UPPERCASE letters" "Großbuchstaben verwenden" \
"Use digits" "Ziffern verwenden" \
"Use hexadecimal digits" "Hexadezimale Ziffern verwenden" \
"Use symbols (%, \$, @, #, etc.)" "Sonderzeichen verwenden (%, \$, @, #, etc.)" \
"Use easy to read characters only (e.g. no \"0\" or \"O\")" "Leicht zu lesende Buchstaben verwenden (z.B. kein \"0\" oder \"O\")" \
"Lock when idle after" "Sperren bei Untätigkeit von" \
"minutes (0=never)" "Minuten (0=niemals)" \
"Auto-save database immediately when changed" "Datenbank sofort nach einer Änderung\n automatisch speichern" \
"Use Password Safe 3 format" "Passwort Safe 3 Format verwenden" \
"V2 Unicode support" "V2 Unicode Unterstützung" \
"Delete Group" "Gruppe löschen" \
"Rename Group ..." "Gruppe umbenennen" \
"Parent Group" "Übergeordnete Gruppe" \
"Group Name" "Gruppename" \
"Notes:" "Notizen" \
"Show Password" "Passwort zeigen" \
"Hide Password" "Passwort verbergen" \
"Generate Password" "Passwort erzeugen" \
"Override\nPassword Policy" "Passwort-Richtlinien\nnicht beachten" \
"Password Length" "Passwortlänge" \
"Use lowercase letters" "Kleinbuchstaben verwenden" \
"Use UPPERCASE letters" "Großbuchstaben verwenden" \
"Use digits" "Ziffern verwenden" \
"Use hexadecimal digits" "Hexadezimale Ziffern verwenden" \
"Use symbols (%, \$, @, #, etc.)" "Sonderzeichen verwenden (%, \$, @, #, etc.)" \
"Use easy to read characters only (e.g. no \"0\" or \"O\")" "Leicht zu lesende Buchstaben verwenden (z.B. kein \"0\" oder \"O\")" \
"Lock when idle after" "Sperren bei Untätigkeit nach" \
"minutes (0=never)" "Minuten (0=niemals)" \
"Auto-save database immediately when changed" "Datenbank sofort nach einer Änderung automatisch speichern" \
"Use Password Safe 3 format" "Passwort Safe 3 Format verwenden" \
"V2 Unicode support" "V2 Unicode Unterstützung" \
"V3 key stretching iterations" "Wiederholungen für das V3 <key stretching>" \
"General" "Allgemein" \
"Clear clipboard after" "Zwischenablage löschen nach" \
"seconds (0=never)" "Sekunden (0=nie)" \
"Remember" "Merke" \
"database names" "Datenbanknamen" \
"Clear" "Löschen" \
"When double clicking a login ..." "Beim Doppelklick auf ein Login" \
"Copy password to clipboard" "Password in die Zwischenablage kopieren" \
"(Not available with v2 database format.)" "Im v2 Datenbank-Format nicht verfügbar" \
"Add Login ..." "Login hinzufügen ..." \
"Language" "Sprache" \
"Cancel" "Abbrechen" \
"Create new password database" "Neue Passwort Datenbank" \
"Edit Login ..." "Login bearbeiten ..." \
"Edit Login" "Login bearbeiten" \
"Delete Login" "Login löschen" \
"Do nothing" "Nichts tun" \
"Backup database on save" "Beim Speichern eine Sicherungskopie anlegen" \
"Remember sizes of dialog boxes" "Die Größe der Dialogboxen merken" \
"Defaults" "Voreinstellungen" \
"Note: these defaults will be applied to\
new databases. To change a setting for an existing\
database, go to \"Database Preferences\" in the \"Manage\"\
menu." "Die Voreinstellungen werden auf neue Datenbanken angewendet. Um die Einstellungen einer existierenden Datenbank zu ändern, wählen Sie \"Datenbank Einstellungen\" im \"Manage\" Menǘ" \
"Include password field" "Passwort-Feld exportieren" \
"Include \"Notes\" field" "Notiz-Feld exportieren" \
"Save as Unicode text file" "Als Unicode-Textfeld speichern" \
"Field separator" "Feldbegrenzer" \
"Show security warning" "Zeige Sicherheitswarnung" \
"Close" "Schließen" \
"<New Database>" "<Neue Datenbank>" \
"Database:" "Datenbank:" \
"Browse" "Suchen" \
"Password:" "Passwort:" \
"Please be patient. Verifying password ..." "Bitte etwas Geduld. Überprüfe Passwort ..." \
"Exit" "Beenden" \
"The Database Is Locked" "Die Datenbank ist gesperrt" \
"Enter the Master Password." "Bitte Master Passwort eingeben." \
"Find" "Suche" \
"Find in ..." "Suche in ..." \
"Any field" "Beliebiges Feld" \
"Title" "Titel" \
"Username" "Benutzername" \
"Case sensitive find" "Suche abhängig von der Groß- und Kleinschreibung" \
"Add Login ..." "Login hinzufügen ..." \
"Welcome to the Password Gorilla." "Hallo bei Password Gorilla" \
"Add/Edit/View Login" "Login hinzufügen/bearbeiten/ansehen" \
"Password database saved as $nativeName" "Die Passwort-Datenbank wurde als $nativeName gespeichert" \
"Password database saved." "Passwort-Datenbank gespeichert." \
"Current Master Password:" "Altes Master Passwort:" \
"New Master Password:" "Neues Master Passwort:" \
"Confirm:" "Bestätigung:" \
"The current password database is modified.\
Do you want to save the database?\n\
\"Yes\" saves the database, and exits.\n\
\"No\" discards all changes, and exits.\n\
\"Cancel\" returns to the main menu." "Die Datenbank wurde modifiziert. Sollen die Änderungen gespeichert werden?" \
"Login unchanged." "Login wurde nicht verändert" \
"Copy Username to Clipboard" "Kopiere Benutzernamen in die Zwischenablage" \
"Copy Password to Clipboard" "Kopiere Passwort in die Zwischenablage" \
"Copy URL to Clipboard" "Kopiere URL in die Zwischenablage" \
"Add Login" "Neuer Login" \
"Add Subgroup" "Neue Untergruppe" \
"Rename Group" "Gruppe umbenennen" \
"Copied password to clipboard." "Passwort in die Zwischenablage kopiert." \
"Using Password Gorilla" "Mit Password Gorilla arbeiten" \
"Database exported." "Datenbank exportiert." \
"Exporting ..." "Exportiere ..." \
"Export password database as text ..." "Exportiere die Datenbank unverschlüsselt ..." \
"Export Security Warning" "Sicherheitshinweis Export" \
"You are about to export the password\
database to a plain-text file. The file will\
not be encrypted or password-protected. Anybody\
with access can read the file, and learn your\
user names and passwords. Make sure to store the\
file in a secure location. Do you want to\
continue?" "Die Datenbank wird unverschlüsselt und ohne\
Passwortschutz zugänglich sein. Sorgen Sie für eine
sichere Aufbewahrung Ihrer Daten. Wollen Sie fortfahren?" \
"Copied user name to clipboard." "Benutzernamen in die Zwischenablage kopiert." \
"Copied URL to clipboard." "URL in die Zwischenablage kopiert." \
"Can not copy URL to clipboard: no URL defined." "Fehler: Keine URL definiert." \
"Clipboard cleared." "Zwischenablage geleert." \
"Any field" "Alle Felder" \
"Find Options ..." "Suchoptionen" \
"Case sensitive find" "Groß/Kleinschreibung beachten" \
"Login deleted." "Login gelöscht." \
"Are you sure that you want to delete this login?" "Soll der Login wirklich gelöscht werden?" \
"Move Login ..." "Login verschieben ..." \
"Move Group ..." "Gruppe verschieben ..." \
"Move" "Verschiebe ..." \
"Group" "Gruppe" \
"Destination Group with format <Group.Subgroup> :" "Zielgruppe im Format <Gruppe.Untergruppe> :" \
"Database Locked" "Datenbank gesperrt" \
"Merge Password Database" "Importiere Passwort-Datenbank" \
"Add logins using <Add Login> in the \"Login\" menu." "Jetzt können Logins angelegt werden." \
"Password database $nativeName loaded." " Passwort-Datenbank geladen." \
"Addition of new login canceled." "Ein neuer Login wurde nicht angelegt." \
"New login added." "Neuer Login angelegt." \
"Login modified." "Login bearbeitet." \
"Group deleted." "Gruppe gelöscht." \
"Group name unchanged." "Gruppenname unverändert." \
"Group renamed." "Gruppe umbenannt." \
"Merging " "Vereinige mit " \
"Welcome back." "Sperre aufgehoben." \
"Password policy changed." "Passwortrichtlinien geändert." \
"Text not found." "Text nicht gefunden." \
"Parent:" "Elterngruppe:" \
"Are you sure that you want to delete group and all its contents?" "Soll die Gruppe wirklich mit dem gesamten Inhalt gelöscht werden?" \
"New Group Name:" "Name der neuen Gruppe:" \
"Notes" "Notizen" \
"Find Text:" "Suchtext:" \
"To create a new database, click cancel, then \"New\" from the \"File\" menu." "Neue Datenbank anlegen mit <Abbrechen>, dann Klick auf <Neue ...> im Menü <Datenbank>" \
}
mcunknown de "unbekannt"

BIN
sources/pics/browse.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 B

BIN
sources/pics/gorilla-16x16.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
sources/pics/gorilla-32x32.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
sources/pics/gorilla-48x48.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

BIN
sources/pics/gorilla-logo.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
sources/pics/gorilla-splash.gif Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

BIN
sources/pics/gorilla.ico Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.9 KiB

29
sources/pwsafe/LICENSE.txt Executable file
View File

@ -0,0 +1,29 @@
Copyright (c) 2004, Frank Pilhofer
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
Neither the name of the package's author nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"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 COPYRIGHT
OWNER 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.

2
sources/pwsafe/pkgIndex.tcl Executable file
View File

@ -0,0 +1,2 @@
package ifneeded pwsafe 0.2 [list source [file join $dir pwsafe.tcl]]

692
sources/pwsafe/pwsafe-db.tcl Executable file
View File

@ -0,0 +1,692 @@
#
# ----------------------------------------------------------------------
# pwsafe::db: holds password records, and provides an API to them
# ----------------------------------------------------------------------
#
namespace eval pwsafe {}
catch {
itcl::delete class pwsafe::db
}
itcl::class pwsafe::db {
#
# field types:
#
# Name Field Type Value Type Comments
# -----------------------------------------------------------
# UUID 1 UUID
# Group 2 Text [2]
# Title 3 Text
# Username 4 Text
# Notes 5 Text
# Password 6 Text
# Creation Time 7 time_t
# Password Modification Time 8 time_t
# Last Access Time 9 time_t
# Password Lifetime 10 time_t [4]
# Password Policy 11 4 bytes [5]
# Last Mod. time 12 time_t
# URL 13 Text
# Autotype 14 Text
#
# [2] The "Group" is meant to support displaying the entries in a
# tree-like manner. Groups can be heirarchical, with elements separated
# by a period, supporting groups such as "Finance.credit cards.Visa".
# This implies that periods entered by the user will have a backslash
# prepended to them. A backslash entered by the user will have another
# backslash prepended.
#
# [4] Password lifetime is in seconds, and a value of zero means
# "forever".
#
# [5] Unused so far
#
# Not all records use all fields. pwsafe-2.05 only seems to use the
# UUID, Group, Title, Username, Nodes and Password fields. I have
# omitted some documentation for the other fields.
#
# For more detail, look at the pwsafe documentation, or, better yet,
# the pwsafe source code -- the documentation seems to be based on
# a good helping of wishful thinking; e.g., it says that all Text
# fields are Unicode, but they are not.
#
#
# Preferences: {type number name registry-name default persistent}
#
# Need to keep in sync with pwsafe's corelib/PWSPrefs.cpp
#
protected common utf8Default 0
protected common utf8PrefNumber 24
protected common allPreferences {
{B 0 AlwaysOnTop alwaysontop 0 1}
{B 1 ShowPWDefault showpwdefault 0 1}
{B 2 ShowPWInList showpwinlist 0 1}
{B 3 SortAscending sortascending 1 1}
{B 4 UseDefUser usedefuser 0 1}
{B 5 SaveImmediately saveimmediately 0 1}
{B 6 PWUseLowercase pwuselowercase 1 1}
{B 7 PWUseUppercase pwuseuppercase 1 1}
{B 8 PWUseDigits pwusedigits 1 1}
{B 9 PWUseSymbols pwusesymbols 0 1}
{B 10 PWUseHexDigits pwusehexdigits 0 1}
{B 11 PWEasyVision pweasyvision 0 1}
{B 12 DontAskQuestion dontaskquestion 0 1}
{B 13 DeleteQuestion deletequestion 0 1}
{B 14 DCShowsPassword DCShowsPassword 0 1}
{B 15 DontAskMinimizeClearYesNo DontAskMinimizeClearYesNo 0 1}
{B 16 DatabaseClear DatabaseClear 0 1}
{B 17 DontAskSaveMinimize DontAskSaveMinimize 0 1}
{B 18 QuerySetDef QuerySetDef 1 1}
{B 19 UseNewToolbar UseNewToolbar 1 1}
{B 20 UseSystemTray UseSystemTray 1 1}
{B 21 LockOnWindowLock LockOnWindowLock 1 1}
{B 22 LockOnIdleTimeout LockOnIdleTimeout 1 1}
{B 23 EscExits EscExits 1 1}
{B 24 IsUTF8 isutf8 0 1}
{I 0 Column1Width column1width -1 0}
{I 1 Column2Width column2width -1 0}
{I 2 Column3Width column3width -1 0}
{I 3 Column4Width column4width -1 0}
{I 4 SortedColumn sortedcolumn 0 1}
{I 5 PWLenDefault pwlendefault 8 1}
{I 6 MaxMRUItems maxmruitems 4 1}
{I 7 IdleTimeout IdleTimeout 5 1}
{S 0 CurrentBackup currentbackup "" 1}
{S 1 CurrentFile currentfile "" 0}
{S 2 LastView lastview "list" 1}
{S 3 DefUserName defusername "" 1}
}
#
# Internal data:
#
# header is an array, the index is <type>
#
# records is an array, the index is <record number>,<type>
#
# Record number and type are both integers. The type has the value
# of the type byte, as identified in the pwsafe "documentation."
# The value of the array element is the field value.
#
# recordnumbers is a list of all record numbers that are available
# in the records array
#
protected variable engine
protected variable password
protected variable header
protected variable preferences
protected variable records
protected variable recordnumbers
protected variable nextrecordnumber
#
# The number of iterations for the key-stretching algorithm in the
# V3 format.
#
public variable keyStretchingIterations
#
# Warnings during opening the file.
#
public variable warningsDuringOpen
#
# constructor
#
constructor {password_} {
set nextrecordnumber 0
set recordnumbers [list]
set engine [namespace current]::[itwofish::ecb #auto \
[pwsafe::int::randomString 16]]
set password [encryptField $password_]
array set preferences {}
array set header {}
set keyStretchingIterations 2048
set warningsDuringOpen [list]
}
#
# Encrypt a field, so that we don't store anything in cleartext
#
private method encryptField {data} {
set dataLen [string length $data]
set msg [pwsafe::int::randomString 4]
append msg [binary format I $dataLen]
append msg $data
incr dataLen 8
if {($dataLen % 16) != 0} {
set padLen [expr {16-($dataLen%16)}]
append msg [pwsafe::int::randomString $padLen]
incr dataLen $padLen
}
set blocks [expr {$dataLen/16}]
set encryptedMsg ""
for {set i 0} {$i < $blocks} {incr i} {
append encryptedMsg [$engine encryptBlock \
[string range $msg [expr {16*$i}] [expr {16*$i+15}]]]
}
pwsafe::int::randomizeVar msg
return $encryptedMsg
}
private method decryptField {encryptedMsg} {
set eml [string length $encryptedMsg]
set blocks [expr {$eml/16}]
set decryptedMsg ""
for {set i 0} {$i < $blocks} {incr i} {
append decryptedMsg [$engine decryptBlock \
[string range $encryptedMsg [expr {16*$i}] [expr {16*$i+15}]]]
}
binary scan $decryptedMsg @4I msgLen
set res [string range $decryptedMsg 8 [expr {7+$msgLen}]]
pwsafe::int::randomizeVar decryptedMsg
return $res
}
#
# Accessors for our data members
#
public method getPassword {} {
return [decryptField $password]
}
public method checkPassword {oldPassword} {
if {![string equal $oldPassword [decryptField $password]]} {
return 0
}
return 1
}
public method setPassword {newPassword} {
set password [encryptField $newPassword]
}
#
# Manage preferences
#
public method getPreferencesAsString {} {
set result ""
set isUTF8 [getPreference "IsUTF8"]
for {set index 0} {$index < [llength $allPreferences]} {incr index} {
set prefItem [lindex $allPreferences $index]
if {![lindex $prefItem 5]} {
# not persistent
continue
}
set prefType [lindex $prefItem 0]
set prefNumber [lindex $prefItem 1]
set prefDefault [lindex $prefItem 4]
if {[info exists preferences($prefType,$prefNumber)]} {
set prefValue $preferences($prefType,$prefNumber)
if {[string length $result] > 0} {
append result " "
}
append result $prefType " " $prefNumber " "
if {$prefType == "B" || $prefType == "I"} {
append result $prefValue
} else {
if {$isUTF8} {
set prefValue [encoding convertto utf-8 $prefValue]
}
append result "\"" [string map {\\ \\\\ \" \\\"} \
$prefValue] "\""
}
}
}
return $result
}
public method setPreferencesFromString {newPreferences} {
#
# String is of the form "X nn vv X nn vv..." Where X=[BIS]
# for binary, integer and string, resp., nn is the numeric
# value of the enum, and vv is the value, {1.0} for bool,
# unsigned integer for int, and quoted string for String.
# Only values != default are stored.
#
set isUTF8 [getPreference "IsUTF8"]
set i 0
while {$i < [string length $newPreferences]} {
set prefType [string index $newPreferences $i]
if {[string is space $prefType]} {
incr i
continue
}
if {$prefType != "B" && $prefType != "I" && \
$prefType != "S"} {
error "unknown preference type: $prefType"
}
#
# Space between preference type and preference number
#
incr i
while {$i < [string length $newPreferences] && \
[string is space [string index $newPreferences $i]]} {
incr i
}
#
# Preference number
#
set prefNumberString ""
while {$i < [string length $newPreferences] && \
[string is digit [string index $newPreferences $i]]} {
append prefNumberString [string index $newPreferences $i]
incr i
}
if {$i >= [string length $newPreferences]} {
error "premature end of preference"
}
if {[scan $prefNumberString "%d" prefNumber] != 1} {
error "expected preference number, got $prefNumberString"
}
#
# Space between preference number and preference value
#
while {$i < [string length $newPreferences] && \
[string is space [string index $newPreferences $i]]} {
incr i
}
#
# Preference value
#
if {$prefType == "B" || $prefType == "I"} {
set prefValString ""
while {$i < [string length $newPreferences] && \
[string is digit [string index $newPreferences $i]]} {
append prefValString [string index $newPreferences $i]
incr i
}
if {[scan $prefValString "%d" prefValue] != 1} {
error "expected number for value, got $prefValString"
}
} elseif {$prefType == "S"} {
if {[string index $newPreferences $i] != "\""} {
error "expected initial quote for string value"
}
incr i
set prefValue ""
while {$i < [string length $newPreferences]} {
set c [string index $newPreferences $i]
if {$c == "\\"} {
append prefValue [string index $newPreferences [incr i]]
} elseif {$c == "\""} {
break
} else {
append prefValue $c
}
incr i
}
if {$i >= [string length $newPreferences]} {
error "premature end of string value"
}
incr i
if {$isUTF8} {
set prefValue [encoding convertfrom utf-8 $prefValue]
}
}
if {$prefType == "B" && $prefNumber == $utf8PrefNumber} {
set isUTF8 $prefValue
}
set preferences($prefType,$prefNumber) $prefValue
}
}
#
# Get/set named preferences
#
public method existsPreference {name} {
for {set index 0} {$index < [llength $allPreferences]} {incr index} {
set prefItem [lindex $allPreferences $index]
set prefType [lindex $prefItem 0]
set prefNumber [lindex $prefItem 1]
set prefName [lindex $prefItem 2]
if {[string equal $prefName $name]} {
if {[info exists preferences($prefType,$prefNumber)]} {
return 1
} else {
return 0
}
}
}
error "no such preference: $name"
}
public method getPreference {name} {
for {set index 0} {$index < [llength $allPreferences]} {incr index} {
set prefItem [lindex $allPreferences $index]
set prefType [lindex $prefItem 0]
set prefNumber [lindex $prefItem 1]
set prefName [lindex $prefItem 2]
if {[string equal $prefName $name]} {
if {[info exists preferences($prefType,$prefNumber)]} {
return $preferences($prefType,$prefNumber)
} else {
return [lindex $prefItem 4]
}
}
}
error "no such preference: $name"
}
public method setPreference {name value} {
for {set index 0} {$index < [llength $allPreferences]} {incr index} {
set prefItem [lindex $allPreferences $index]
set prefType [lindex $prefItem 0]
set prefNumber [lindex $prefItem 1]
set prefName [lindex $prefItem 2]
if {[string equal $prefName $name]} {
if {$value != [lindex $prefItem 4]} {
set preferences($prefType,$prefNumber) $value
} elseif {[info exists preferences($prefType,$prefNumber)]} {
unset preferences($prefType,$prefNumber)
}
return
}
}
error "no such preference: $name"
}
#
# Helper: split a hierarchical group name into its components
#
public proc splitGroup {group} {
#
# Elements are separated by a period. When a group name contains
# a period, it is escaped by a backslash. For that to work, a
# backslash is also escaped, i.e., group "\." becomes "\\\.".
#
# If the hierarchical name does not contain any slashes, we can
# simply use split.
#
if {[string first "\\" $group] == -1} {
set result [split $group .]
foreach element $result {
if {$element == ""} {
error "group name can not be empty"
}
}
return $result
}
#
# Have to parse ...
#
set result [list]
set element ""
for {set index 0} {$index < [string length $group]} {incr index} {
set c [string index $group $index]
if {$c == "\\"} {
append element [string index $group [incr index]]
} elseif {$c == "."} {
if {$element == ""} {
error "group name can not be empty"
}
lappend result $element
set element ""
} else {
append element $c
}
}
if {$element == ""} {
error "group name can not be empty"
}
lappend result $element
return $result
}
#
# Helper: concatenate a list of groups into a hierarchical name
#
public proc concatGroups {groups} {
set result ""
set index 0
foreach element $groups {
if {$index > 0} {
append result "."
}
if {$element == ""} {
error "group name can not be empty"
}
append result [string map {\\ \\\\ . \\.} $element]
incr index
}
return $result
}
#
# Reserve a recordnumber
#
public method createRecord {} {
set nn [incr nextrecordnumber]
lappend recordnumbers $nn
return $nn
}
#
# Delete a record
#
public method deleteRecord {rn} {
set index [lsearch -exact -integer $recordnumbers $rn]
if {$index != -1} {
set recordnumbers [lreplace $recordnumbers $index $index]
array unset records $rn,*
}
}
#
# Does a specific record number exist?
#
public method existsRecord {rn} {
if {[lsearch -exact -integer $recordnumbers $rn] == -1} {
return 0
}
return 1
}
#
# Get all record numbers
#
public method getAllRecordNumbers {} {
return [lsort -integer $recordnumbers]
}
#
# Does a specific record have a specific field
#
public method existsField {rn field} {
if {![info exists records($rn,$field)]} {
if {![existsRecord $rn]} {
error "record $rn does not exist"
}
return 0
}
return 1
}
#
# Get a list of all fields that are available for a record
#
public method getFieldsForRecord {rn} {
set names [array names records -glob $rn,*]
if {[llength $names] == 0} {
error "record $rn does not exist"
}
set result [list]
foreach name $names {
lappend result [lindex [split $name ,] 1]
}
return [lsort -integer $result]
}
#
# Get the value of a field
#
public method getFieldValue {rn field} {
if {![info exists records($rn,$field)]} {
if {![existsRecord $rn]} {
error "record $rn does not exist"
}
error "record $rn does not have field $field"
}
if {$field == 2 || $field == 3 || $field == 4 || \
$field == 5 || $field == 6} {
# text fields
return [encoding convertfrom utf-8 \
[decryptField $records($rn,$field)]]
}
return [decryptField $records($rn,$field)]
}
#
# Set the value of a field
#
public method setFieldValue {rn field value} {
if {![existsRecord $rn]} {
error "record $rn does not exist"
}
if {$field == 2 || $field == 3 || $field == 4 || \
$field == 5 || $field == 6} {
# text fields
set records($rn,$field) [encryptField \
[encoding convertto utf-8 $value]]
} else {
set records($rn,$field) [encryptField $value]
}
}
#
# Unset the value of a field. Deletes the record if this was the
# last field.
#
public method unsetFieldValue {rn field} {
if {![existsRecord $rn]} {
return
}
if {[info exists records($rn,$field)]} {
pwsafe::int::randomizeVar records($rn,$field)
unset records($rn,$field)
if {[llength [getFieldsForRecord $rn]] == 0} {
deleteRecord $rn
}
}
}
#
# Get the value of a header field
#
public method hasHeaderField {field} {
if {$field == 2} {
return 1
}
return [info exists header($field)]
}
public method getHeaderField {field} {
if {$field == 2} {
#
# Preferences
#
return [getPreferencesAsString]
}
if {![info exists header($field)]} {
error "no header field $field"
}
return $header($field)
}
#
# Set the value of a header field
#
public method setHeaderField {field value} {
if {$field == 2} {
#
# Preferences
#
setPreferencesFromString $value
return
}
set header($field) $value
}
#
# Get all header field types
#
public method getAllHeaderFields {} {
set fields [array names header]
if {![info exists header(2)]} {
#
# There is always a preferences field.
#
lappend fields 2
}
return [lsort -integer $fields]
}
}

270
sources/pwsafe/pwsafe-int.tcl Executable file
View File

@ -0,0 +1,270 @@
#
# ----------------------------------------------------------------------
# pwsafe internal helpers
# ----------------------------------------------------------------------
#
namespace eval pwsafe {}
namespace eval pwsafe::int {}
variable pwsafe::int::sha1isz_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
}
#
# This SHA1 implementation is taken from Don Libes' version
# in tcllib. The only difference is the "isz" parameter; if
# set to true, the "initial H buffer" is set to all zeroes
# instead of the well-defined constants. Oh, and the result
# is returned in binary format, not in hex.
#
# pwsafe calls this SHA1_init_state_zero, and uses it to
# compute a hash to validate the password with. It is almost
# certainly due to a bug in an early pwsafe implementation
# that later versions still want to be compatible with.
#
proc pwsafe::int::sha1isz {msg {isz 0}} {
variable sha1isz_K
#
# 4. MESSAGE PADDING
#
# pad to 512 bits (512/8 = 64 bytes)
set msgLen [string length $msg]
# last 8 bytes are reserved for msgLen
# plus 1 for "1"
set padLen [expr {56 - $msgLen%64}]
if {$msgLen % 64 >= 56} {
incr padLen 64
}
# 4a. and b. append single 1b followed by 0b's
append msg [binary format "a$padLen" \200]
# 4c. append 64-bit length
# Our implementation obviously limits string length to 32bits.
append msg \0\0\0\0[binary format "I" [expr {8*$msgLen}]]
#
# 7. COMPUTING THE MESSAGE DIGEST
#
# initial H buffer
if {!$isz} {
set H0 [expr {int(0x67452301)}]
set H1 [expr {int(0xEFCDAB89)}]
set H2 [expr {int(0x98BADCFE)}]
set H3 [expr {int(0x10325476)}]
set H4 [expr {int(0xC3D2E1F0)}]
} else {
set H0 0
set H1 0
set H2 0
set H3 0
set H4 0
}
#
# process message in 16-word blocks (64-byte blocks)
#
# convert message to array of 32-bit integers
# each block of 16-words is stored in M($i,0-16)
binary scan $msg I* words
set blockLen [llength $words]
for {set i 0} {$i < $blockLen} {incr i 16} {
# 7a. Divide M[i] into 16 words W[0], W[1], ...
set W [lrange $words $i [expr {$i+15}]]
# 7b. For t = 16 to 79 let W[t] = ....
set t 16
set t3 12
set t8 7
set t14 1
set t16 -1
for {} {$t < 80} {incr t} {
set x [expr {[lindex $W [incr t3]] ^ [lindex $W [incr t8]] ^ \
[lindex $W [incr t14]] ^ [lindex $W [incr t16]]}]
lappend W [expr {($x << 1) | (($x >> 31) & 1)}]
}
# 7c. Let A = H[0] ....
set A $H0
set B $H1
set C $H2
set D $H3
set E $H4
# 7d. For t = 0 to 79 do
for {set t 0} {$t < 20} {incr t} {
set TEMP [expr {(($A << 5) | (($A >> 27) & 0x1f)) + \
(($B & $C) | ((~$B) & $D)) \
+ $E + [lindex $W $t] + [lindex $sha1isz_K $t]}]
set E $D
set D $C
set C [expr {($B << 30) | (($B >> 2) & 0x3fffffff)}]
set B $A
set A $TEMP
}
for {} {$t<40} {incr t} {
set TEMP [expr {(($A << 5) | (($A >> 27) & 0x1f)) + \
($B ^ $C ^ $D) \
+ $E + [lindex $W $t] + [lindex $sha1isz_K $t]}]
set E $D
set D $C
set C [expr {($B << 30) | (($B >> 2) & 0x3fffffff)}]
set B $A
set A $TEMP
}
for {} {$t<60} {incr t} {
set TEMP [expr {(($A << 5) | (($A >> 27) & 0x1f)) + \
(($B & $C) | ($B & $D) | ($C & $D)) \
+ $E + [lindex $W $t] + [lindex $sha1isz_K $t]}]
set E $D
set D $C
set C [expr {($B << 30) | (($B >> 2) & 0x3fffffff)}]
set B $A
set A $TEMP
}
for {} {$t<80} {incr t} {
set TEMP [expr {(($A << 5) | (($A >> 27) & 0x1f)) + \
($B ^ $C ^ $D) \
+ $E + [lindex $W $t] + [lindex $sha1isz_K $t]}]
set E $D
set D $C
set C [expr {($B << 30) | (($B >> 2) & 0x3fffffff)}]
set B $A
set A $TEMP
}
set H0 [expr {int(($H0 + $A) & 0xffffffff)}]
set H1 [expr {int(($H1 + $B) & 0xffffffff)}]
set H2 [expr {int(($H2 + $C) & 0xffffffff)}]
set H3 [expr {int(($H3 + $D) & 0xffffffff)}]
set H4 [expr {int(($H4 + $E) & 0xffffffff)}]
}
return [binary format IIIII $H0 $H1 $H2 $H3 $H4]
}
#
# pwsafe 2 uses blowfish incorrectly. Blowfish wants big endian
# integers (i.e., \x00\x00\x00\x01 becomes 1). Pwsafe just
# casts char* to long*, which does the Wrong Thing on a little
# endian architecture (like x86). So we frequently have to
# change a number's sex.
#
proc pwsafe::int::genderbender {val} {
binary scan $val i* vals
return [binary format I* $vals]
}
#
# (For Password Safe 2)
# H(RND) is SHA1_init_state_zero(tempSalt|Cipher(RND));
# tempSalt = SHA1(RND|{0x00,0x00}|password);
# Cipher(RND) is 1000 encryptions of RND, with tempSalt as the
# encryption key. In short, a kind of HMAC dependant on the
# password. Written before the HMAC RFC came out, no good reason
# to change. (If it ain't broke...)
#
proc pwsafe::int::computeHRND {RND password} {
set temp $RND
append temp "\x00\x00"
append temp $password
set tempSalt [pwsafe::int::sha1isz $temp]
set engine [iblowfish::ecb \#auto $tempSalt]
set cipher [pwsafe::int::genderbender $RND]
for {set i 0} {$i < 1000} {incr i} {
set cipher [$engine encryptBlock $cipher]
}
itcl::delete object $engine
set temp [pwsafe::int::genderbender $cipher]
append temp "\x00\x00"
return [pwsafe::int::sha1isz $temp 1]
}
#
# Password Safe 3 uses a "stretched key" of the user's passphrase and
# the SALT, as defined by the hash-function-based key stretching
# algorithm in http://www.schneier.com/paper-low-entropy.pdf
# (Section 4.1), with SHA-256 as the hash function, and a variable
# number of iterations that is stored in the file.
#
proc pwsafe::int::computeStretchedKey {salt password iterations} {
set st [sha2::SHA256Init]
sha2::SHA256Update $st $password
sha2::SHA256Update $st $salt
set Xi [sha2::SHA256Final $st]
for {set i 0} {$i < $iterations} {incr i} {
set Xi [sha2::sha256 -bin $Xi]
}
return $Xi
}
#
# Generate a string of pseudo-random data
#
proc pwsafe::int::randomString {length} {
set randomOctets [list]
#
# Use ISAAC PRNG, if present
#
if {[namespace exists ::isaac]} {
for {set i 0} {$i < $length} {incr i} {
set rand [::isaac::rand]
lappend randomOctets [expr {127-int($rand*256.)}]
}
} else {
for {set i 0} {$i < $length} {incr i} {
lappend randomOctets [expr {127-int(rand()*256.)}]
}
}
return [binary format c* $randomOctets]
}
#
# Overwrite a variable's contents with a random string
#
proc pwsafe::int::randomizeVar {args} {
foreach var $args {
uplevel 1 "set $var \[pwsafe::int::randomString \[string length \$$var\]\]"
}
}

145
sources/pwsafe/pwsafe-io.tcl Executable file
View File

@ -0,0 +1,145 @@
#
# ----------------------------------------------------------------------
# pwsafe::io::streamreader: reads from a Tcl stream
# pwsafe::io::streamwrite: writes to a Tcl stream
# ----------------------------------------------------------------------
#
# Note: stream must not be non-blocking
#
catch {
itcl::delete class pwsafe::io::streamreader
itcl::delete class pwsafe::io::streamwriter
}
itcl::class pwsafe::io::streamreader {
protected variable stream
protected variable sz
public method read {numChars} {
return [::read $stream $numChars]
}
public method eof {} {
return [::eof $stream]
}
public method tell {} {
return [::tell $stream]
}
public method size {} {
return $sz
}
constructor {stream_ sz_} {
set stream $stream_
set sz $sz_
}
}
itcl::class pwsafe::io::streamwriter {
protected variable stream
public method write {data} {
return [::puts -nonewline $stream $data]
}
constructor {stream_} {
set stream $stream_
}
}
#
# ----------------------------------------------------------------------
# pwsafe::io::stringreader: reads from a string
# pwsafe::io::stringwriter: writes to a string
# ----------------------------------------------------------------------
#
catch {
itcl::delete class pwsafe::io::stringreader
itcl::delete class pwsafe::io::stringwriter
}
itcl::class pwsafe::io::stringreader {
protected variable data
protected variable index
public method read {numChars} {
if {$index >= [string length $data]} {
return ""
}
set result [string range $data $index [expr {$index + $numChars - 1}]]
incr index $numChars
return $result
}
public method eof {} {
if {$index >= [string length $data]} {
return 1
}
return 0
}
public method tell {} {
return $index
}
public method size {} {
return [string length $data]
}
constructor {data_} {
set data $data_
set index 0
}
}
itcl::class pwsafe::io::stringwriter {
public variable data
public method write {x} {
append data $x
}
}
#
# ----------------------------------------------------------------------
# Dump a human redably formatted record to a Tcl output stream
# ----------------------------------------------------------------------
#
proc pwsafe::io::dumpRecord {db out rn} {
set fields [$db getFieldsForRecord $rn]
puts $out "Record \# $rn"
foreach field [lsort -integer $fields] {
set value [$db getFieldValue $rn $field]
switch -- $field {
1 {
puts $out " UUID: $value"
}
2 {
puts $out " Group: $value"
}
3 {
puts $out " Title: $value"
}
4 {
puts $out " Username: $value"
}
5 {
set value [string map {\n {\n }} $value]
puts $out " Notes: $value"
}
6 {
puts $out " Password: $value"
}
default {
set fn "<$field>"
puts $out " [format %4s $fn]: $value"
}
}
}
}

713
sources/pwsafe/pwsafe-v2.tcl Executable file
View File

@ -0,0 +1,713 @@
#
# ----------------------------------------------------------------------
# pwsafe::v2::reader: reads an existing file from a stream
# ----------------------------------------------------------------------
#
namespace eval pwsafe::v2 {}
catch {
itcl::delete class pwsafe::v2::reader
}
itcl::class pwsafe::v2::reader {
#
# An object of type pwsafe::db, to read into
#
protected variable db
#
# object to read data from, using its "read <numChars>" method
#
protected variable source
#
# An object of type iblowfish::cbc
#
protected variable engine
#
# We can not be reused; complain if someone tries to
#
protected variable used
#
# read one field; returns [list type data]; or empty list on eof
#
protected method readField {} {
#
# first block contains field length and type
#
set encryptedLength [$source read 8]
if {[string length $encryptedLength] == 0 && [$source eof]} {
return [list]
}
if {[string length $encryptedLength] != 8} {
error "less than 8 bytes remaining for length field"
}
set fixedEncryptedLength [pwsafe::int::genderbender $encryptedLength]
set decryptedLength [$engine decrypt $fixedEncryptedLength]
set fixedDecryptedLength [pwsafe::int::genderbender $decryptedLength]
if {[binary scan $fixedDecryptedLength ic fieldLength fieldType] != 2} {
error "oops"
}
#
# field length sanity check
#
if {$fieldLength < 0 || $fieldLength > 65536} {
error "field length $fieldLength looks insane"
}
#
# data is padded to 8 bytes
#
set numBlocks [expr {($fieldLength + 7) / 8}]
if {$numBlocks == 0} {
set numBlocks 1
}
set dataLength [expr {$numBlocks * 8}]
#
# decrypt field
#
set encryptedData [$source read $dataLength]
if {[string length $encryptedData] != $dataLength} {
error "out of data"
}
set fixedEncryptedData [pwsafe::int::genderbender $encryptedData]
set decryptedData [$engine decrypt $fixedEncryptedData]
set fixedDecryptedData [pwsafe::int::genderbender $decryptedData]
#
# adjust length of data; truncate padding
#
set fieldData [string range $fixedDecryptedData 0 [expr {$fieldLength - 1}]]
#
# field decrypted successfully
#
pwsafe::int::randomizeVar decryptedData fixedDecryptedData
return [list $fieldType $fieldData]
}
protected method readAllFields {{percentvar ""}} {
if {$percentvar != ""} {
upvar $percentvar pcv
}
set fileSize [$source size]
#
# Format Description Block:
#
# Name: " !!!Version 2 File Format!!! " [...]
# Password: "pre-2.0"
# Notes: Used to store preferences
#
set nameField [readField]
set passField [readField]
set prefField [readField]
#
# Check if the nameField matches the expected magic. If not, then
# this is likely a file in pre-2.0 format.
#
set v2magic [string range [lindex $nameField 1] 1 27]
if {$v2magic != "!!!Version 2 File Format!!!"} {
#
# Version 1 file?
#
while {![$source eof]} {
set filePos [$source tell]
if {$filePos != -1 && $fileSize != -1 && \
$fileSize != 0 && $filePos <= $fileSize} {
set percent [expr {100.0*double($filePos)/double($fileSize)}]
} else {
set percent -1
}
set pcv $percent
set recordnumber [$db createRecord]
#
# The name contains both the title and user name, separated by
# "SPLTCHR" '\xad'. If the user name is "DEFUSERCHR" '\xa0', it
# is supposed to be replaced by the default user name - which
# we don't support yet.
#
# When Password Safe 2.x exports a file as 1.x, it prepends the
# group name to the title. That seems too much of a borderline
# case to support.
#
set titleAndUser [split [lindex $nameField 1] "\xad"]
if {[llength $titleAndUser] == 1} {
$db setFieldValue $recordnumber 3 [lindex $titleAndUser 0]
} elseif {[llength $titleAndUser] == 2} {
$db setFieldValue $recordnumber 3 [lindex $titleAndUser 0]
if {![string equal [lindex $titleAndUser 1] "\xa0"]} {
$db setFieldValue $recordnumber 4 [lindex $titleAndUser 1]
}
} else {
error "V1 name field looks suspect"
}
$db setFieldValue $recordnumber 6 [lindex $passField 1]
$db setFieldValue $recordnumber 5 [lindex $prefField 1]
pwsafe::int::randomizeVar titleAndUser nameField passField prefField
set nameField [readField]
if {[llength $nameField] == 0} {
# eof
break
}
set passField [readField]
set prefField [readField]
}
return
}
#
# Set preferences
#
$db setPreferencesFromString [lindex $prefField 1]
#
# Using UTF-8?
#
set isUTF8 [$db getPreference "IsUTF8"]
#
# Remaining fields are user data
#
set first 1
while {![$source eof]} {
set field [readField]
if {[llength $field] == 0} {
# eof
break
}
set filePos [$source tell]
if {$filePos != -1 && $fileSize != -1 && \
$fileSize != 0 && $filePos <= $fileSize} {
set percent [expr {100.0*double($filePos)/double($fileSize)}]
} else {
set percent -1
}
set pcv $percent
set fieldType [lindex $field 0]
set fieldValue [lindex $field 1]
if {$fieldType == -1} {
set first 1
continue
}
if {$first} {
set recordnumber [$db createRecord]
set first 0
}
#
# Format the field's type, if necessary
#
switch -- $fieldType {
1 {
#
# UUID
#
binary scan $fieldValue H* tmp
set fieldValue [string range $tmp 0 7]
append fieldValue "-" [string range $tmp 8 11]
append fieldValue "-" [string range $tmp 12 15]
append fieldValue "-" [string range $tmp 16 19]
append fieldValue "-" [string range $tmp 20 31]
}
2 -
3 -
4 -
6 -
13 {
#
# Text fields may be stored in UTF-8
#
if {$isUTF8} {
set fieldValue [encoding convertfrom utf-8 $fieldValue]
}
}
5 {
#
# Notes field uses CRLF for line breaks, we want LF only.
#
if {$isUTF8} {
set fieldValue [encoding convertfrom utf-8 $fieldValue]
}
set fieldValue [string map {\r\n \n} $fieldValue]
}
7 -
8 -
9 -
10 -
12 {
#
# (7) Creation Time, (8) Password Modification Time,
# (9) Last Access Time, (10) Password Lifetime and
# (11) Last Modification Time are of type time_t,
# i.e., a 4 byte (little endian) integer
#
if {[binary scan $fieldValue i fieldValue] != 1} {
continue
}
#
# Make unsigned
#
set fieldValue [expr {($fieldValue + 0x100000000) % 0x100000000}]
#
# On Mac, we have to adjust for the different epoch
#
if {[info exists ::tcl_platform(platform)] && \
[string equal $::tcl_platform(platform) "macintosh"]} {
incr fieldValue 2082844800
}
}
}
$db setFieldValue $recordnumber $fieldType $fieldValue
pwsafe::int::randomizeVar fieldType fieldValue
}
}
public method readFile {{percentvar ""}} {
if {$used} {
error "this object can not be reused"
}
set used 1
if {$percentvar != ""} {
upvar $percentvar pcv
set pcvp "pcv"
} else {
set pcvp ""
}
#
# The file is laid out as follows:
#
# RND|H(RND)|SALT|IP|
#
# RND is an 8 byte random value, used along with H(RND) to quickly
# verify the password.
#
# SALT is the salt used for encrypting the data
#
# IP is the initial initialization vector value
#
set rnd [$source read 8]
set hrnd [$source read 20]
set salt [$source read 20]
set ip [$source read 8]
if {[string length $rnd] != 8 || \
[string length $hrnd] != 20 || \
[string length $salt] != 20 || \
[string length $ip] != 8} {
pwsafe::int::randomizeVar rnd hrnd salt ip
error "end of file while reading header"
}
#
# Verify the password
#
set myhrnd [pwsafe::int::computeHRND $rnd [$db getPassword]]
if {![string equal $hrnd $myhrnd]} {
pwsafe::int::randomizeVar rnd salt ip myhrnd
error "wrong password"
}
pwsafe::int::randomizeVar rnd hrnd myhrnd
#
# the Blowfish key is SHA1(passphrase|salt)
#
set temp [$db getPassword]
append temp $salt
set key [pwsafe::int::sha1isz $temp]
pwsafe::int::randomizeVar temp salt
#
# Create decryption engine using key and initialization vector
#
set engine [iblowfish::cbc \#auto $key [pwsafe::int::genderbender $ip]]
pwsafe::int::randomizeVar key ip
readAllFields $pcvp
itcl::delete object $engine
set engine ""
}
constructor {db_ source_} {
set db $db_
set source $source_
set engine ""
set used 0
}
destructor {
if {$engine != ""} {
itcl::delete object $engine
}
}
}
#
# ----------------------------------------------------------------------
# pwsafe::v2::writer: writes to a stream
# ----------------------------------------------------------------------
#
catch {
itcl::delete class pwsafe::v2::writer
}
itcl::class pwsafe::v2::writer {
#
# The object of type pwsafe::db to dump records from
#
protected variable db
#
# object to write data from, using its "write <numChars>" method
#
protected variable sink
#
# An object of type iblowfish::cbc
#
protected variable engine
#
# We can not be reused; complain if someone tries to
#
protected variable used
#
# write one field
#
protected method writeField {fieldType fieldData} {
#
# first 8 byte block contains field length and type
#
set fieldDataLength [string length $fieldData]
set data [binary format ic $fieldDataLength $fieldType]
append data "\0\0\0"
#
# append fieldData
#
append data $fieldData
#
# pad to 8 bytes
#
if {$fieldDataLength == 0} {
# there must be at least one block of data
append data "\x00\x00\x00\x00\x00\x00\x00\x00"
} elseif {[expr {$fieldDataLength % 8}] != 0} {
set padLength [expr {7-($fieldDataLength % 8)}]
append data [string range \
"\x00\x00\x00\x00\x00\x00\x00\x00" \
0 $padLength]
}
#
# assert length
#
set l [string length $data]
if {[expr {$l%8}] != 0} {
error "oops"
}
#
# encrypt data
#
set swappedData [pwsafe::int::genderbender $data]
set encryptedData [$engine encrypt $swappedData]
set swappedEncryptedData [pwsafe::int::genderbender $encryptedData]
#
# write encrypted data
#
$sink write $swappedEncryptedData
pwsafe::int::randomizeVar data swappedData encryptedData \
swappedEncryptedData
}
protected method writeAllFields {{percentvar ""}} {
if {$percentvar != ""} {
upvar $percentvar pcv
}
#
# Format Description Block:
#
# Name: " !!!Version 2 File Format!!! " [...]
# Password: "pre-2.0"
# Notes: Used to store preferences
#
set magic " !!!Version 2 File Format!!! "
append magic "Please upgrade to PasswordSafe 2.0"
append magic " or later"
writeField 0 $magic
writeField 6 "2.0"
writeField 5 [$db getPreferencesAsString]
#
# Using UTF-8?
#
set isUTF8 [$db getPreference "IsUTF8"]
#
# Dump all records
#
set allRecords [$db getAllRecordNumbers]
set numRecords [llength $allRecords]
set countRecords 0
foreach recordNumber $allRecords {
incr countRecords
set pcv [expr {100.0*double($countRecords)/double($numRecords)}]
foreach fieldType [$db getFieldsForRecord $recordNumber] {
set fieldValue [$db getFieldValue $recordNumber $fieldType]
set ignoreField 0
switch -- $fieldType {
1 {
#
# UUID
#
set fieldValue [string map {- {}} $fieldValue]
set fieldValue [binary format H* $fieldValue]
}
2 -
3 -
4 -
6 -
13 {
#
# Text fields may be stored in UTF-8
#
if {$isUTF8} {
set fieldValue [encoding convertto utf-8 $fieldValue]
}
if {$fieldValue == ""} {
set ignoreField 1
}
}
5 {
#
# Notes field uses CRLF for line breaks, we want LF only.
#
if {$isUTF8} {
set fieldValue [encoding convertto utf-8 $fieldValue]
}
set fieldValue [string map {\n \r\n} $fieldValue]
if {$fieldValue == ""} {
set ignoreField 1
}
}
7 -
8 -
9 -
10 -
12 {
#
# (7) Creation Time, (8) Password Modification Time,
# (9) Last Access Time, (10) Password Lifetime and
# (11) Last Modification Time are of type time_t,
# i.e., a 4 byte (little endian) integer
#
#
# Make unsigned
#
set fieldValue [expr {($fieldValue + 0x100000000) % 0x100000000}]
#
# On Mac, we have to adjust for the different epoch
#
if {[info exists ::tcl_platform(platform)] && \
[string equal $::tcl_platform(platform) "macintosh"]} {
incr fieldValue -2082844800
}
#
# Make 32 bit, and encode to binary
#
set fieldValue [expr {$fieldValue & 0xffffffff}]
set fieldValue [binary format i $fieldValue]
}
}
if {$ignoreField} {
continue
}
writeField $fieldType $fieldValue
pwsafe::int::randomizeVar fieldType fieldValue
}
writeField -1 ""
}
}
public method writeFile {{percentvar ""}} {
if {$used} {
error "this object can not be reused"
}
set used 1
if {$percentvar != ""} {
upvar $percentvar pcv
set pcvp "pcv"
} else {
set pcvp ""
}
#
# The file is laid out as follows:
#
# RND|H(RND)|SALT|IP|
#
# RND is an 8 byte random value, used along with H(RND) to quickly
# verify the password.
#
# SALT is the salt used for encrypting the data
#
# IP is the initial initialization vector value
#
set rnd [pwsafe::int::randomString 8]
set hrnd [pwsafe::int::computeHRND $rnd [$db getPassword]]
set salt [pwsafe::int::randomString 20]
set ip [pwsafe::int::randomString 8]
$sink write $rnd
$sink write $hrnd
$sink write $salt
$sink write $ip
pwsafe::int::randomizeVar rnd hrnd
#
# the Blowfish key is SHA1(passphrase|salt)
#
set temp [$db getPassword]
append temp $salt
set key [pwsafe::int::sha1isz $temp]
pwsafe::int::randomizeVar temp salt
#
# Create encryption engine using key and initialization vector
#
set engine [iblowfish::cbc \#auto $key [pwsafe::int::genderbender $ip]]
pwsafe::int::randomizeVar key ip
writeAllFields $pcvp
itcl::delete object $engine
set engine ""
}
constructor {db_ sink_} {
set db $db_
set sink $sink_
set engine ""
set used 0
}
destructor {
if {$engine != ""} {
itcl::delete object $engine
}
}
}

878
sources/pwsafe/pwsafe-v3.tcl Executable file
View File

@ -0,0 +1,878 @@
#
# ----------------------------------------------------------------------
# pwsafe::v3::reader: reads an existing file from a stream
# ----------------------------------------------------------------------
#
namespace eval pwsafe::v3 {}
catch {
itcl::delete class pwsafe::v3::reader
}
itcl::class pwsafe::v3::reader {
#
# An object of type pwsafe::db, to read into
#
protected variable db
#
# Object to read data from, using its "read <numChars>" method
#
protected variable source
#
# An object of type itwofish::cbc
#
protected variable engine
#
# The HMAC-SHA 256 engine
#
protected variable hmacEngine
#
# We can not be reused; complain if someone tries to
#
protected variable used
#
# Read one field; returns [list type data]; or empty list on eof
#
protected method readField {} {
#
# first block contains field length and type
#
set encryptedFirstBlock [$source read 16]
if {$encryptedFirstBlock == "PWS3-EOFPWS3-EOF"} {
# EOF marker
return [list]
}
if {[string length $encryptedFirstBlock] == 0 && [$source eof]} {
error "EOF while reading field"
}
if {[string length $encryptedFirstBlock] != 16} {
error "less than 16 bytes remaining for first block"
}
set decryptedFirstBlock [$engine decrypt $encryptedFirstBlock]
if {[binary scan $decryptedFirstBlock ic fieldLength fieldType] != 2} {
error "oops"
}
#
# field length sanity check
#
if {$fieldLength < 0 || $fieldLength > 65536} {
error "field length $fieldLength looks insane"
}
#
# remainder of the first block contains data
#
if {$fieldLength <= 11} {
set fieldData [string range $decryptedFirstBlock 5 [expr {$fieldLength + 4}]]
pwsafe::int::randomizeVar decryptedFirstBlock
return [list $fieldType $fieldData]
}
set fieldData [string range $decryptedFirstBlock 5 end]
pwsafe::int::randomizeVar decryptedFirstBlock
incr fieldLength -11
#
# remaining data is stored in multiple blocks
#
set numBlocks [expr {($fieldLength + 15) / 16}]
set dataLength [expr {$numBlocks * 16}]
#
# decrypt field
#
set encryptedData [$source read $dataLength]
if {[string length $encryptedData] != $dataLength} {
error "out of data"
}
set decryptedData [$engine decrypt $encryptedData]
#
# adjust length of data; truncate padding
#
append fieldData [string range $decryptedData 0 [expr {$fieldLength - 1}]]
#
# field decrypted successfully
#
pwsafe::int::randomizeVar decryptedData
return [list $fieldType $fieldData]
}
protected method readHeaderFields {} {
#
# Read header fields.
#
while {![$source eof]} {
set field [readField]
set fieldType [lindex $field 0]
set fieldValue [lindex $field 1]
if {$fieldType == -1} {
break
}
sha2::HMACUpdate $hmacEngine $fieldValue
#
# Format the header's field type, if necessary
#
switch -- $fieldType {
0 {
#
# Version
#
binary scan $fieldValue cc minor major
set fieldValue [list $major $minor]
}
1 {
#
# UUID
#
binary scan $fieldValue H* tmp
set fieldValue [string range $tmp 0 7]
append fieldValue "-" [string range $tmp 8 11]
append fieldValue "-" [string range $tmp 12 15]
append fieldValue "-" [string range $tmp 16 19]
append fieldValue "-" [string range $tmp 20 31]
}
}
$db setHeaderField $fieldType $fieldValue
}
#
# If there is no version header field, then add one. The rest of
# the code uses it to detect v3 files, assuming v2 otherwise.
#
if {![$db hasHeaderField 0]} {
$db setHeaderField 0 [list 3 0]
}
}
protected method readAllFields {{percentvar ""}} {
if {$percentvar != ""} {
upvar $percentvar pcv
}
set fileSize [$source size]
#
# Remaining fields are user data
#
set first 1
while {![$source eof]} {
set field [readField]
if {[llength $field] == 0} {
# eof
break
}
set filePos [$source tell]
if {$filePos != -1 && $fileSize != -1 && \
$fileSize != 0 && $filePos <= $fileSize} {
set percent [expr {100.0*double($filePos)/double($fileSize)}]
} else {
set percent -1
}
set pcv $percent
set fieldType [lindex $field 0]
set fieldValue [lindex $field 1]
if {$fieldType == -1} {
set first 1
continue
}
if {$first} {
set recordnumber [$db createRecord]
set first 0
}
sha2::HMACUpdate $hmacEngine $fieldValue
#
# Format the field's type, if necessary
#
switch -- $fieldType {
1 {
#
# UUID
#
binary scan $fieldValue H* tmp
set fieldValue [string range $tmp 0 7]
append fieldValue "-" [string range $tmp 8 11]
append fieldValue "-" [string range $tmp 12 15]
append fieldValue "-" [string range $tmp 16 19]
append fieldValue "-" [string range $tmp 20 31]
}
2 -
3 -
4 -
6 -
13 {
#
# Text fields are always stored in UTF-8
#
set fieldValue [encoding convertfrom utf-8 $fieldValue]
}
5 {
#
# Notes field uses CRLF for line breaks, we want LF only.
#
set fieldValue [encoding convertfrom utf-8 $fieldValue]
set fieldValue [string map {\r\n \n} $fieldValue]
}
7 -
8 -
9 -
10 -
12 {
#
# (7) Creation Time, (8) Password Modification Time,
# (9) Last Access Time, (10) Password Lifetime and
# (11) Last Modification Time are of type time_t,
# i.e., a 4 byte (little endian) integer
#
if {[binary scan $fieldValue i fieldValue] != 1} {
continue
}
#
# Make unsigned
#
set fieldValue [expr {($fieldValue + 0x100000000) % 0x100000000}]
#
# On Mac, we have to adjust for the different epoch
#
if {[info exists ::tcl_platform(platform)] && \
[string equal $::tcl_platform(platform) "macintosh"]} {
incr fieldValue 2082844800
}
}
}
$db setFieldValue $recordnumber $fieldType $fieldValue
pwsafe::int::randomizeVar fieldType fieldValue
}
}
public method readFile {{percentvar ""}} {
if {$used} {
error "this object can not be reused"
}
set used 1
if {$percentvar != ""} {
upvar $percentvar pcv
set pcvp "pcv"
} else {
set pcvp ""
}
#
# The file is laid out as follows:
#
# TAG|SALT|ITER|H(P')|B1|B2|B3|B4|IV|
#
# TAG is the sequence of 4 ASCII characters "PWS3"
#
# SALT is a 256 bit random value
#
# ITER is the number of iterations for the password stretching.
#
# P' is the "stretched key" H_<ITER>(Password,SALT)
#
# B1 and B2 are two 128-bit blocks encrypted with Twofish using
# P' as the key, in ECB mode.
#
# B3 and B4 are two 128-bit blocks encrypted with Twofish using
# P' as the key, in ECB mode.
#
# IV is the 128-bit random initial value for CBC mode.
#
set tag [$source read 4]
if {$tag != "PWS3"} {
error "file does not have PWS3 magic"
}
set salt [$source read 32]
set biter [$source read 4]
set hskey [$source read 32]
set b1 [$source read 16]
set b2 [$source read 16]
set b3 [$source read 16]
set b4 [$source read 16]
set iv [$source read 16]
if {[string length $salt] != 32 || \
[string length $biter] != 4 || \
[string length $hskey] != 32 || \
[string length $b1] != 16 || \
[string length $b2] != 16 || \
[string length $b3] != 16 || \
[string length $b4] != 16 || \
[string length $iv] != 16} {
pwsafe::int::randomizeVar salt hskey b1 b2 b3 b4 iv
error "end of file while reading header"
}
#
# Verify the password
#
if {[binary scan $biter i iter] != 1} {
error "oops"
}
if {$iter > 65536} {
#
# More than 65536 iterations looks unreasonable, at least today.
# Sounds more like a file format bug. We don't want to spend such
# a long time iterating, appearing dead.
#
error "Key stretching wants $iter iterations, which seems unreasonable"
}
if {$iter < 2048} {
#
# Low security. Warn.
#
set dbWarnings [$db cget -warningsDuringOpen]
lappend dbWarnings "File only uses low-security $iter iterations\
for key stretching; at least 2048 recommended."
$db configure -warningsDuringOpen $dbWarnings
}
$db configure -keyStretchingIterations $iter
set myskey [pwsafe::int::computeStretchedKey $salt [$db getPassword] $iter]
set myhskey [sha2::sha256 -bin $myskey]
if {![string equal $hskey $myhskey]} {
pwsafe::int::randomizeVar salt hskey b1 b2 b3 b4 iv myskey myhskey
error "wrong password"
}
pwsafe::int::randomizeVar salt hskey myhskey
#
# The real key is encrypted using Twofish in ECB mode, using
# the stretched passphrase as its key.
#
set hdrEngine [itwofish::ecb \#auto $myskey]
pwsafe::int::randomizeVar myskey
#
# Decrypt the real key from b1 and b2, and the key L that is
# used to calculate the HMAC
#
set key [$hdrEngine decryptBlock $b1]
append key [$hdrEngine decryptBlock $b2]
pwsafe::int::randomizeVar b1 b2
set hmacKey [$hdrEngine decryptBlock $b3]
append hmacKey [$hdrEngine decryptBlock $b4]
set hmacEngine [sha2::HMACInit $hmacKey]
pwsafe::int::randomizeVar b3 b4 hmacKey
itcl::delete object $hdrEngine
#
# Create decryption engine using key and initialization vector
#
set engine [itwofish::cbc \#auto $key $iv]
pwsafe::int::randomizeVar key iv
#
# Read data
#
if {[catch {
readHeaderFields
readAllFields $pcvp
} oops]} {
set errorInfo $::errorInfo
sha2::HMACFinal $hmacEngine
itcl::delete object $engine
set engine ""
error $oops $errorInfo
}
#
# Read and validate HMAC
#
set hmac [$source read 32]
set myHmac [sha2::HMACFinal $hmacEngine]
if {![string equal $hmac $myHmac]} {
set dbWarnings [$db cget -warningsDuringOpen]
lappend dbWarnings "Database authentication failed. File may\
have been tampered with."
$db configure -warningsDuringOpen $dbWarnings
}
pwsafe::int::randomizeVar hmac myHmac
itcl::delete object $engine
set engine ""
}
constructor {db_ source_} {
set db $db_
set source $source_
set engine ""
set used 0
}
destructor {
if {$engine != ""} {
itcl::delete object $engine
}
}
}
#
# ----------------------------------------------------------------------
# pwsafe::v3::writer: writes to a stream
# ----------------------------------------------------------------------
#
catch {
itcl::delete class pwsafe::v3::writer
}
itcl::class pwsafe::v3::writer {
#
# The object of type pwsafe::db to dump records from
#
protected variable db
#
# object to write data from, using its "write <numChars>" method
#
protected variable sink
#
# An object of type itwofish::cbc
#
protected variable engine
#
# The HMAC-SHA 256 engine
#
protected variable hmacEngine
#
# We can not be reused; complain if someone tries to
#
protected variable used
#
# Write one field
#
protected method writeField {fieldType fieldData} {
#
# First 16 byte block contains field length, type, and up to 16
# bytes of data
#
set fieldDataLength [string length $fieldData]
set data [binary format ic $fieldDataLength $fieldType]
#
# Append fieldData
#
append data $fieldData
#
# Pad to 16 bytes
#
set dataLength [expr {$fieldDataLength + 5}]
if {($dataLength % 16) != 0} {
set padLength [expr {15-($dataLength % 16)}]
append data [string range [pwsafe::int::randomString 15] 0 $padLength]
}
#
# Assert length
#
set l [string length $data]
if {[expr {$l%16}] != 0} {
error "oops"
}
#
# Encrypt data
#
set encryptedData [$engine encrypt $data]
#
# Write encrypted data
#
$sink write $encryptedData
pwsafe::int::randomizeVar data encryptedData
}
protected method writeHeaderFields {} {
#
# Password Safe 3.01 requires that header fields 0 (version),
# 1 (UUID) and 2 (non-default preferences) are all present, and
# in exactly this order. Make sure to please it.
#
#
# Version: 3.0
#
$db setHeaderField 0 [list 3 0]
if {![$db hasHeaderField 1]} {
#
# Default dummy UUID. (Password Safe 3.01 ignores it. So do we.)
#
$db setHeaderField 1 00000000-0000-0000-0000-000000000000
}
#
# No need to set field 2. There is always a preferences string.
#
#
# Write header fields
#
foreach fieldType [$db getAllHeaderFields] {
set fieldValue [$db getHeaderField $fieldType]
switch -- $fieldType {
0 {
#
# Version
#
set major [lindex $fieldValue 0]
set minor [lindex $fieldValue 1]
set fieldValue [binary format cc $minor $major]
}
1 {
#
# UUID
#
set fieldValue [string map {- {}} $fieldValue]
set fieldValue [binary format H* $fieldValue]
}
}
writeField $fieldType $fieldValue
sha2::HMACUpdate $hmacEngine $fieldValue
}
#
# End of header
#
writeField -1 ""
}
protected method writeAllFields {{percentvar ""}} {
if {$percentvar != ""} {
upvar $percentvar pcv
}
#
# Dump all records
#
set allRecords [$db getAllRecordNumbers]
set numRecords [llength $allRecords]
set countRecords 0
foreach recordNumber $allRecords {
incr countRecords
set pcv [expr {100.0*double($countRecords)/double($numRecords)}]
foreach fieldType [$db getFieldsForRecord $recordNumber] {
set fieldValue [$db getFieldValue $recordNumber $fieldType]
set ignoreField 0
switch -- $fieldType {
1 {
#
# UUID
#
set fieldValue [string map {- {}} $fieldValue]
set fieldValue [binary format H* $fieldValue]
}
2 -
3 -
4 -
6 -
13 {
#
# Text fields are always stored in UTF-8
#
set fieldValue [encoding convertto utf-8 $fieldValue]
if {$fieldValue == ""} {
set ignoreField 1
}
}
5 {
#
# Notes field uses CRLF for line breaks, we want LF only.
#
set fieldValue [encoding convertto utf-8 $fieldValue]
set fieldValue [string map {\n \r\n} $fieldValue]
if {$fieldValue == ""} {
set ignoreField 1
}
}
7 -
8 -
9 -
10 -
12 {
#
# (7) Creation Time, (8) Password Modification Time,
# (9) Last Access Time, (10) Password Lifetime and
# (11) Last Modification Time are of type time_t,
# i.e., a 4 byte (little endian) integer
#
#
# Make unsigned
#
set fieldValue [expr {($fieldValue + 0x100000000) % 0x100000000}]
#
# On Mac, we have to adjust for the different epoch
#
if {[info exists ::tcl_platform(platform)] && \
[string equal $::tcl_platform(platform) "macintosh"]} {
incr fieldValue -2082844800
}
#
# Make 32 bit, and encode to binary
#
set fieldValue [expr {$fieldValue & 0xffffffff}]
set fieldValue [binary format i $fieldValue]
}
}
if {$ignoreField} {
continue
}
writeField $fieldType $fieldValue
sha2::HMACUpdate $hmacEngine $fieldValue
pwsafe::int::randomizeVar fieldType fieldValue
}
writeField -1 ""
}
}
public method writeFile {{percentvar ""}} {
if {$used} {
error "this object can not be reused"
}
set used 1
if {$percentvar != ""} {
upvar $percentvar pcv
set pcvp "pcv"
} else {
set pcvp ""
}
#
# The file is laid out as follows:
#
# TAG|SALT|ITER|H(P')|B1|B2|B3|B4|IV|
#
# TAG is the sequence of 4 ASCII characters "PWS3"
#
# SALT is a 256 bit random value
#
# ITER is the number of iterations for the password stretching.
#
# P' is the "stretched key" H_<ITER>(Password,SALT)
#
# B1 and B2 are two 128-bit blocks encrypted with Twofish using
# P' as the key, in ECB mode.
#
# B3 and B4 are two 128-bit blocks encrypted with Twofish using
# P' as the key, in ECB mode.
#
# IV is the 128-bit random initial value for CBC mode.
#
set salt [pwsafe::int::randomString 32]
set iter [$db cget -keyStretchingIterations]
set skey [pwsafe::int::computeStretchedKey $salt [$db getPassword] $iter]
set hskey [sha2::sha256 -bin $skey]
$sink write "PWS3"
$sink write $salt
$sink write [binary format i $iter]
$sink write $hskey
#
# The real key is encrypted using Twofish in ECB mode, using
# the stretched passphrase as its key.
#
set hdrEngine [itwofish::ecb \#auto $skey]
pwsafe::int::randomizeVar skey
set k1 [pwsafe::int::randomString 16]
set k2 [pwsafe::int::randomString 16]
set h1 [pwsafe::int::randomString 16]
set h2 [pwsafe::int::randomString 16]
set b1 [$hdrEngine encryptBlock $k1]
set b2 [$hdrEngine encryptBlock $k2]
set b3 [$hdrEngine encryptBlock $h1]
set b4 [$hdrEngine encryptBlock $h2]
::itcl::delete object $hdrEngine
$sink write $b1
$sink write $b2
$sink write $b3
$sink write $b4
set key $k1
append key $k2
set hmacKey $h1
append hmacKey $h2
pwsafe::int::randomizeVar k1 k2 h1 h2
#
# Create encryption engine
#
set iv [pwsafe::int::randomString 16]
$sink write $iv
set engine [itwofish::cbc \#auto $key $iv]
set hmacEngine [sha2::HMACInit $hmacKey]
pwsafe::int::randomizeVar iv key hmacKey
#
# Write data
#
writeHeaderFields
writeAllFields $pcvp
#
# Write EOF marker
#
$sink write "PWS3-EOFPWS3-EOF"
#
# Write HMAC
#
$sink write [sha2::HMACFinal $hmacEngine]
itcl::delete object $engine
set engine ""
}
constructor {db_ sink_} {
set db $db_
set sink $sink_
set engine ""
set used 0
}
destructor {
if {$engine != ""} {
itcl::delete object $engine
}
}
}

BIN
sources/pwsafe/pwsafe.dat Executable file

Binary file not shown.

268
sources/pwsafe/pwsafe.tcl Executable file
View File

@ -0,0 +1,268 @@
package require Tcl 8.4
package require Itcl
package require sha256
package require iblowfish
package require itwofish
#
# ----------------------------------------------------------------------
# This file contains the public API for the pwsafe package
# ----------------------------------------------------------------------
#
namespace eval pwsafe {}
#
# ----------------------------------------------------------------------
# createFromStream: create a pwsafe::db object from a stream object
# ----------------------------------------------------------------------
#
proc pwsafe::createFromStream {stream password version {percentvar ""}} {
if {$percentvar != ""} {
upvar $percentvar pcv
set pcvp "pcv"
} else {
set pcvp ""
}
set db [namespace current]::[pwsafe::db #auto $password]
if {$version == 3} {
set reader [namespace current]::[pwsafe::v3::reader #auto $db $stream]
} else {
set reader [namespace current]::[pwsafe::v2::reader #auto $db $stream]
}
if {[catch {$reader readFile $pcvp} oops]} {
set origErrorInfo $::errorInfo
itcl::delete object $reader
itcl::delete object $db
error $oops $origErrorInfo
}
itcl::delete object $reader
return $db
}
#
# ----------------------------------------------------------------------
# createFromFile: create a pwsafe object from a file
# ----------------------------------------------------------------------
#
proc pwsafe::createFromFile {fileName password {percentvar ""}} {
if {$percentvar != ""} {
upvar $percentvar pcv
set pcvp "pcv"
} else {
set pcvp ""
}
if {[catch {set size [file size $fileName]}]} {
set size -1
}
set file [open $fileName "r"]
fconfigure $file -translation binary
#
# Check if the file begins with the Password Save 3.x "PWS3" magic.
#
set magic [::read $file 4]
::seek $file 0
set stream [namespace current]::[pwsafe::io::streamreader #auto $file $size]
if {[catch {
if {[string equal $magic "PWS3"]} {
set db [pwsafe::createFromStream $stream $password 3 $pcvp]
} else {
set db [pwsafe::createFromStream $stream $password 2 $pcvp]
}
} oops]} {
set origErrorInfo $::errorInfo
itcl::delete object $stream
catch {close $file}
error $oops $origErrorInfo
}
itcl::delete object $stream
if {[catch {close $file} oops]} {
itcl::delete object $db
error $oops
}
return $db
}
#
# ----------------------------------------------------------------------
# createFromString: create a pwsafe object from an (in-memory) string
# ----------------------------------------------------------------------
#
proc pwsafe::createFromString {data password {percentvar ""}} {
if {$percentvar != ""} {
upvar $percentvar pcv
set pcvp "pcv"
} else {
set pcvp ""
}
#
# Check if the string begins with the Password Save 3.x "PWS3" magic.
#
set stream [namespace current]::[pwsafe::io::stringreader #auto $data]
if {[catch {
if {[string equal -length 4 $data "PWS3"]} {
set db [pwsafe::createFromStream $stream $password 3 $pcvp]
} else {
set db [pwsafe::createFromStream $stream $password 2 $pcvp]
}
} oops]} {
set origErrorInfo $::errorInfo
itcl::delete object $stream
error $oops $origErrorInfo
}
itcl::delete object $stream
return $db
}
#
# ----------------------------------------------------------------------
# writeToFile: write a pwsafe object to a file
# ----------------------------------------------------------------------
#
proc pwsafe::writeToFile {db fileName version {percentvar ""}} {
if {$percentvar != ""} {
upvar $percentvar pcv
set pcvp "pcv"
} else {
set pcvp ""
}
#
# Write to a temporary file first, then make sure that the
# real destination file does not exist (delete if it does),
# then rename the file. This way, the existing database is
# not lost, if something goes wrong.
#
set tmpFileName $fileName
append tmpFileName ".tmp"
set file [open $tmpFileName "w"]
fconfigure $file -translation binary
set stream [namespace current]::[pwsafe::io::streamwriter #auto $file]
if {$version == 3} {
set writer [namespace current]::[pwsafe::v3::writer #auto $db $stream]
} elseif {$version == 2} {
set writer [namespace current]::[pwsafe::v2::writer #auto $db $stream]
} else {
error "invalid version $version"
}
if {[catch {$writer writeFile $pcvp} oops]} {
set origErrorInfo $::errorInfo
itcl::delete object $writer
itcl::delete object $stream
catch {close $file}
catch {file delete $tmpFileName}
error $oops $origErrorInfo
}
itcl::delete object $writer
itcl::delete object $stream
close $file
#
# Done writing to temporary file.
#
if {[file exists $fileName]} {
file delete -- $fileName
}
file rename -- $tmpFileName $fileName
}
#
# ----------------------------------------------------------------------
# writeToString: write a pwsafe object to a string
# ----------------------------------------------------------------------
#
proc pwsafe::writeToString {db version {percentvar ""}} {
if {$percentvar != ""} {
upvar $percentvar pcv
set pcvp "pcv"
} else {
set pcvp ""
}
set stream [namespace current]::[pwsafe::io::stringwriter #auto]
if {$version == 3} {
set writer [namespace current]::[pwsafe::v3::writer #auto $db $stream]
} elseif {$version == 2} {
set writer [namespace current]::[pwsafe::v2::writer #auto $db $stream]
} else {
error "invalid version $version"
}
if {[catch {$writer writeFile $pcvp} oops]} {
set origErrorInfo $::errorInfo
itcl::delete object $writer
itcl::delete object $stream
catch {close $file}
error $oops $origErrorInfo
}
set result [$stream cget -data]
itcl::delete object $writer
itcl::delete object $stream
return $result
}
#
# ----------------------------------------------------------------------
# pwsafe::dumpAllRecords: print all records in a human readable manner
# ----------------------------------------------------------------------
#
proc pwsafe::dumpAllRecords {db out} {
foreach rn [$db getAllRecordNumbers] {
pwsafe::io::dumpRecord $db $out $rn
}
}
#
# ----------------------------------------------------------------------
# Source the pwsafe::db implementation and the various helpers
# ----------------------------------------------------------------------
#
set pwsafeDir [file dirname [info script]]
source [file join $pwsafeDir "pwsafe-int.tcl"]
source [file join $pwsafeDir "pwsafe-db.tcl"]
source [file join $pwsafeDir "pwsafe-io.tcl"]
source [file join $pwsafeDir "pwsafe-v2.tcl"]
source [file join $pwsafeDir "pwsafe-v3.tcl"]
#
# ----------------------------------------------------------------------
# finishing touches
# ----------------------------------------------------------------------
#
package provide pwsafe 0.2

50
sources/pwsafe/pwtest.tcl Executable file
View File

@ -0,0 +1,50 @@
#! /bin/sh
# the next line restarts using tclsh \
exec tclsh84 "$0" ${1+"$@"}
set myDir [file normalize [file dirname [info script]]]
set myDirDir [file normalize [file dirname $myDir]]
lappend auto_path $myDir
lappend auto_path [file join $myDirDir sha1]
lappend auto_path [file join $myDirDir blowfish]
lappend auto_path [file join $myDirDir twofish]
package require sha256
package require iblowfish
package require itwofish
package require pwsafe
package require base64
set b64testfile {
QmWWZUVopydNWoFFyGLFAzrBGPOTn077gTBwQl5A+eNVJDil0K3p7xyKTgsq
domg2GcDhHIID9HGO/NNxljIOeiYIsAV10xi9HadvqaKeR6qns3dkW3M19YA
L6XSfvDLzAM3U51Q2CwY+56ValsIr93qTX8/XoeqXIXT0c/eWeYTdbihylVt
te2gjTDqOmfITU66F8ZQ16fyUerdqHCfT+TOLeKU5FijLt5ga4sXhzbR1O7S
Qgxt1BM0JFosViPeUcD+uRJrrZ6FQNFJn53LDtmWuGrlrg3Dnm4/7Ez3mp0h
vBGSueusZbxHjfnomP8QX58ZPZLxUkdzJYkpIn6hkLw2HNiH++WlNDr78GKM
eK2vkY1A5neoQn5XBWJnpET04W9g3Tl26xDR3Dpg/0Q/rdcc/d/aLonkMcyv
SnoKuzP9DpLhleArYdbuilE+QUFZP/pqpxhdvSkXCUCGNqYL8QBjRglV1uIl
u/4mPzomaU5y9TTr+ffQ4zcS4dAdaQMorXtyjoSAJEriJsYn8WWQm+mP26ws
XyrJMc7BrW1ZIzBZQ5cIK/xTwSQGjZ1jxanRyltsbbnGT9Yq+Ai+6Wh9g/PJ
yzLZTJGZr8nPNAupTyJ6MIoEFs9/LEsBou/mR04a0tgC4R+ZnKHh7rbgqPu2
C6OPnyAyLhibuqYLVHLa78ZNcvfKj+b7mcmgj7sk9RVJySmAbIp0ZyE7BnQG
GVnBTe5hL/YYHTFdPlsrTRuHN9upXOgEU84cmpOb8+HqScQfDyj6fSAh0a4w
QgA35tCxjlVPfpk+heBdnz2L/j8MSVxIvLkkcRrCVUt0ohiTbKOIxtPGJfVt
Dx50iEMj41CQrU5cqDnWHBfnCf1wm+Yr+GYJjnwbJRMrumox9earwppQmO+Y
Vuej//btqHEiy0Oy+jm1FKep5+1LaNZHCRbfT4a8TSnX3cjormg9ZEd1ZxD6
aL50DUM9ELUb6rgpwcwRn3KcmD5DNHrNG0N97fRA20hQ1bv0FCl+iuB4kv4t
FnQlblnWmtvbYZx99gPCx2K0GIEmWPUL/qMIjjG/JvTH2YZf/PB/Pb7vOj48
wwerNRTuRiEW2F0CQS2oJkokjmIBLesbrMCAxI12n6mdcF+gaMX+aNZfk8Aj
W18iYvV7270k
}
set testfile [base64::decode $b64testfile]
set db [pwsafe::createFromString $testfile TESTKEY]
# pwsafe::dumpAllRecords $db stdout
set data [pwsafe::writeToString $db 3]
set db2 [pwsafe::createFromString $data TESTKEY]
pwsafe::dumpAllRecords $db2 stdout
itcl::delete object $db

43
sources/sha1/LICENSE.txt Executable file
View File

@ -0,0 +1,43 @@
[The implementation of the SHA256 algorithm in this directory was
copied from the tcllib package. Visit http://tcllib.sourceforge.net/
for more information on tcllib. Below is the contents of tcllib's
license.terms file.]
This software is copyrighted by Ajuba Solutions and other parties.
The following terms apply to all files associated with the software unless
explicitly disclaimed in individual files.
The authors hereby grant permission to use, copy, modify, distribute,
and license this software and its documentation for any purpose, provided
that existing copyright notices are retained in all copies and that this
notice is included verbatim in any distributions. No written agreement,
license, or royalty fee is required for any of the authorized uses.
Modifications to this software may be copyrighted by their authors
and need not follow the licensing terms described here, provided that
the new terms are clearly indicated on the first page of each file where
they apply.
IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY PARTY
FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY
DERIVATIVES THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE
IS PROVIDED ON AN "AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE
NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
MODIFICATIONS.
GOVERNMENT USE: If you are acquiring this software on behalf of the
U.S. government, the Government shall have only "Restricted Rights"
in the software and related documentation as defined in the Federal
Acquisition Regulations (FARs) in Clause 52.227.19 (c) (2). If you
are acquiring the software on behalf of the Department of Defense, the
software shall be classified as "Commercial Computer Software" and the
Government shall have only "Restricted Rights" as defined in Clause
252.227-7013 (c) (1) of DFARs. Notwithstanding the foregoing, the
authors grant the U.S. Government and others acting in its behalf
permission to use and distribute the software in accordance with the
terms specified in this license.

2
sources/sha1/pkgIndex.tcl Executable file
View File

@ -0,0 +1,2 @@
if {![package vsatisfies [package provide Tcl] 8.2]} {return}
package ifneeded sha256 1.0.1 [list source [file join $dir sha256.tcl]]

639
sources/sha1/sha256.tcl Executable file
View File

@ -0,0 +1,639 @@
# sha256.tcl - Copyright (C) 2005 Pat Thoyts <patthoyts@users.sourceforge.net>
#
# SHA1 defined by FIPS 180-2, "The Secure Hash Standard"
# HMAC defined by RFC 2104, "Keyed-Hashing for Message Authentication"
#
# This is an implementation of the secure hash algorithms specified in the
# FIPS 180-2 document.
#
# This implementation permits incremental updating of the hash and
# provides support for external compiled implementations using critcl.
#
# Ref: http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf
#http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf
#
# -------------------------------------------------------------------------
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
# -------------------------------------------------------------------------
#
package require Tcl 8.2; # tcl minimum version
namespace eval ::sha2 {
variable version 1.0.1
variable rcsid {$Id: sha256.tcl,v 1.1 2006/03/22 03:20:39 Owner Exp $}
namespace export sha256 hmac SHA256Init SHA256Update SHA256Final
variable uid
if {![info exists uid]} {
set uid 0
}
variable K
if {![info exists K]} {
# FIPS 180-2: 4.2.2 SHA-256 constants
set K [list \
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 \
]
}
}
# -------------------------------------------------------------------------
# SHA256Init --
#
# Create and initialize an SHA256 state variable. This will be
# cleaned up when we call SHA256Final
#
proc ::sha2::SHA256Init {} {
variable uid
set token [namespace current]::[incr uid]
upvar #0 $token tok
# FIPS 180-2: 5.3.2 Setting the initial hash value
array set tok \
[list \
A [expr {int(0x6a09e667)}] \
B [expr {int(0xbb67ae85)}] \
C [expr {int(0x3c6ef372)}] \
D [expr {int(0xa54ff53a)}] \
E [expr {int(0x510e527f)}] \
F [expr {int(0x9b05688c)}] \
G [expr {int(0x1f83d9ab)}] \
H [expr {int(0x5be0cd19)}] \
n 0 i "" v 256]
return $token
}
# SHA256Update --
#
# This is called to add more data into the hash. You may call this
# as many times as you require. Note that passing in "ABC" is equivalent
# to passing these letters in as separate calls -- hence this proc
# permits hashing of chunked data
#
# If we have a C-based implementation available, then we will use
# it here in preference to the pure-Tcl implementation.
#
proc ::sha2::SHA256Update {token data} {
upvar #0 $token state
# Update the state values
incr state(n) [string length $data]
append state(i) $data
# Calculate the hash for any complete blocks
set len [string length $state(i)]
for {set n 0} {($n + 64) <= $len} {} {
SHA256Transform $token [string range $state(i) $n [incr n 64]]
}
# Adjust the state for the blocks completed.
set state(i) [string range $state(i) $n end]
return
}
# SHA256Final --
#
# This procedure is used to close the current hash and returns the
# hash data. Once this procedure has been called the hash context
# is freed and cannot be used again.
#
# Note that the output is 256 bits represented as binary data.
#
proc ::sha2::SHA256Final {token} {
upvar #0 $token state
SHA256Penultimate $token
# Output
set r [bytes $state(A)][bytes $state(B)][bytes $state(C)][bytes $state(D)][bytes $state(E)][bytes $state(F)][bytes $state(G)][bytes $state(H)]
unset state
return $r
}
# SHA256Penultimate --
#
#
proc ::sha2::SHA256Penultimate {token} {
upvar #0 $token state
# FIPS 180-2: 5.1.1: Padding the message
#
set len [string length $state(i)]
set pad [expr {56 - ($len % 64)}]
if {$len % 64 > 56} {
incr pad 64
}
if {$pad == 0} {
incr pad 64
}
append state(i) [binary format a$pad \x80]
# Append length in bits as big-endian wide int.
set dlen [expr {8 * $state(n)}]
append state(i) [binary format II 0 $dlen]
# Calculate the hash for the remaining block.
set len [string length $state(i)]
for {set n 0} {($n + 64) <= $len} {} {
SHA256Transform $token [string range $state(i) $n [incr n 64]]
}
}
# -------------------------------------------------------------------------
proc ::sha2::SHA224Init {} {
variable uid
set token [namespace current]::[incr uid]
upvar #0 $token tok
# FIPS 180-2 (change notice 1) (1): SHA-224 initialization values
array set tok \
[list \
A [expr {int(0xc1059ed8)}] \
B [expr {int(0x367cd507)}] \
C [expr {int(0x3070dd17)}] \
D [expr {int(0xf70e5939)}] \
E [expr {int(0xffc00b31)}] \
F [expr {int(0x68581511)}] \
G [expr {int(0x64f98fa7)}] \
H [expr {int(0xbefa4fa4)}] \
n 0 i "" v 224]
return $token
}
interp alias {} ::sha2::SHA224Update {} ::sha2::SHA256Update
proc ::sha2::SHA224Final {token} {
upvar #0 $token state
SHA256Penultimate $token
# Output
set r [bytes $state(A)][bytes $state(B)][bytes $state(C)][bytes $state(D)][bytes $state(E)][bytes $state(F)][bytes $state(G)]
unset state
return $r
}
# -------------------------------------------------------------------------
# HMAC Hashed Message Authentication (RFC 2104)
#
# hmac = H(K xor opad, H(K xor ipad, text))
#
# HMACInit --
#
# This is equivalent to the SHA1Init procedure except that a key is
# added into the algorithm
#
proc ::sha2::HMACInit {K} {
# Key K is adjusted to be 64 bytes long. If K is larger, then use
# the SHA1 digest of K and pad this instead.
set len [string length $K]
if {$len > 64} {
set tok [SHA256Init]
SHA256Update $tok $K
set K [SHA256Final $tok]
set len [string length $K]
}
set pad [expr {64 - $len}]
append K [string repeat \0 $pad]
# Cacluate the padding buffers.
set Ki {}
set Ko {}
binary scan $K i16 Ks
foreach k $Ks {
append Ki [binary format i [expr {$k ^ 0x36363636}]]
append Ko [binary format i [expr {$k ^ 0x5c5c5c5c}]]
}
set tok [SHA256Init]
SHA256Update $tok $Ki; # initialize with the inner pad
# preserve the Ko value for the final stage.
# FRINK: nocheck
set [subst $tok](Ko) $Ko
return $tok
}
# HMACUpdate --
#
# Identical to calling SHA256Update
#
proc ::sha2::HMACUpdate {token data} {
SHA256Update $token $data
return
}
# HMACFinal --
#
# This is equivalent to the SHA256Final procedure. The hash context is
# closed and the binary representation of the hash result is returned.
#
proc ::sha2::HMACFinal {token} {
upvar #0 $token state
set tok [SHA256Init]; # init the outer hashing function
SHA256Update $tok $state(Ko); # prepare with the outer pad.
SHA256Update $tok [SHA256Final $token]; # hash the inner result
return [SHA256Final $tok]
}
# -------------------------------------------------------------------------
# Description:
# This is the core SHA1 algorithm. It is a lot like the MD4 algorithm but
# includes an extra round and a set of constant modifiers throughout.
#
set ::sha2::SHA256Transform_body {
variable K
upvar #0 $token state
# FIPS 180-2: 6.2.2 SHA-256 Hash computation.
binary scan $msg I* blocks
set blockLen [llength $blocks]
for {set i 0} {$i < $blockLen} {incr i 16} {
set W [lrange $blocks $i [expr {$i+15}]]
# FIPS 180-2: 6.2.2 (1) Prepare the message schedule
# For t = 16 to 64
# let Wt = (sigma1(Wt-2) + Wt-7 + sigma0(Wt-15) + Wt-16)
set t2 13
set t7 8
set t15 0
set t16 -1
for {set t 16} {$t < 64} {incr t} {
lappend W [expr {[sigma1 [lindex $W [incr t2]]] \
+ [lindex $W [incr t7]] \
+ [sigma0 [lindex $W [incr t15]]] \
+ [lindex $W [incr t16]]}]
}
# FIPS 180-2: 6.2.2 (2) Initialise the working variables
set A $state(A)
set B $state(B)
set C $state(C)
set D $state(D)
set E $state(E)
set F $state(F)
set G $state(G)
set H $state(H)
# FIPS 180-2: 6.2.2 (3) Do permutation rounds
# For t = 0 to 63 do
# T1 = h + SIGMA1(e) + Ch(e,f,g) + Kt + Wt
# T2 = SIGMA0(a) + Maj(a,b,c)
# h = g; g = f; f = e; e = d + T1; d = c; c = b; b = a;
# a = T1 + T2
#
for {set t 0} {$t < 64} {incr t} {
set T1 [expr {($H + [SIGMA1 $E] + [Ch $E $F $G]
+ [lindex $K $t] + [lindex $W $t]) & 0xffffffff}]
set T2 [expr {([SIGMA0 $A] + [Maj $A $B $C]) & 0xffffffff}]
set H $G
set G $F
set F $E
set E [expr {($D + $T1) & 0xffffffff}]
set D $C
set C $B
set B $A
set A [expr {($T1 + $T2) & 0xffffffff}]
}
# FIPS 180-2: 6.2.2 (4) Compute the intermediate hash
incr state(A) $A
incr state(B) $B
incr state(C) $C
incr state(D) $D
incr state(E) $E
incr state(F) $F
incr state(G) $G
incr state(H) $H
}
return
}
# -------------------------------------------------------------------------
# FIPS 180-2: 4.1.2 equation 4.2
proc ::sha2::Ch {x y z} {
return [expr {($x & $y) ^ (~$x & $z)}]
}
# FIPS 180-2: 4.1.2 equation 4.3
proc ::sha2::Maj {x y z} {
return [expr {($x & $y) ^ ($x & $z) ^ ($y & $z)}]
}
# FIPS 180-2: 4.1.2 equation 4.4
# (x >>> 2) ^ (x >>> 13) ^ (x >>> 22)
proc ::sha2::SIGMA0 {x} {
return [expr {[>>> $x 2] ^ [>>> $x 13] ^ [>>> $x 22]}]
}
# FIPS 180-2: 4.1.2 equation 4.5
# (x >>> 6) ^ (x >>> 11) ^ (x >>> 25)
proc ::sha2::SIGMA1 {x} {
return [expr {[>>> $x 6] ^ [>>> $x 11] ^ [>>> $x 25]}]
}
# FIPS 180-2: 4.1.2 equation 4.6
# s0 = (x >>> 7) ^ (x >>> 18) ^ (x >> 3)
proc ::sha2::sigma0 {x} {
#return [expr {[>>> $x 7] ^ [>>> $x 18] ^ (($x >> 3) & 0x1fffffff)}]
return [expr {((($x<<25) | (($x>>7) & (0x7FFFFFFF>>6))) \
^ (($x<<14) | (($x>>18) & (0x7FFFFFFF>>17))) & 0xFFFFFFFF) \
^ (($x>>3) & 0x1fffffff)}]
}
# FIPS 180-2: 4.1.2 equation 4.7
# s1 = (x >>> 17) ^ (x >>> 19) ^ (x >> 10)
proc ::sha2::sigma1 {x} {
#return [expr {[>>> $x 17] ^ [>>> $x 19] ^ (($x >> 10) & 0x003fffff)}]
return [expr {((($x<<15) | (($x>>17) & (0x7FFFFFFF>>16))) \
^ (($x<<13) | (($x>>19) & (0x7FFFFFFF>>18))) & 0xFFFFFFFF) \
^ (($x >> 10) & 0x003fffff)}]
}
# 32bit rotate-right
proc ::sha2::>>> {v n} {
return [expr {(($v << (32 - $n)) \
| (($v >> $n) & (0x7FFFFFFF >> ($n - 1)))) \
& 0xFFFFFFFF}]
}
# 32bit rotate-left
proc ::sha2::<<< {v n} {
return [expr {((($v << $n) \
| (($v >> (32 - $n)) \
& (0x7FFFFFFF >> (31 - $n))))) \
& 0xFFFFFFFF}]
}
# -------------------------------------------------------------------------
# We speed up the SHA256Transform code while maintaining readability in the
# source code by substituting inline for a number of functions.
# The idea is to reduce the number of [expr] calls.
# Inline the Ch function
regsub -all -line \
{\[Ch (\$[ABCDEFGH]) (\$[ABCDEFGH]) (\$[ABCDEFGH])\]} \
$::sha2::SHA256Transform_body \
{((\1 \& \2) ^ ((~\1) \& \3))} \
::sha2::SHA256Transform_body
# Inline the Maj function
regsub -all -line \
{\[Maj (\$[ABCDEFGH]) (\$[ABCDEFGH]) (\$[ABCDEFGH])\]} \
$::sha2::SHA256Transform_body \
{((\1 \& \2) ^ (\1 \& \3) ^ (\2 \& \3))} \
::sha2::SHA256Transform_body
# Inline the SIGMA0 function
regsub -all -line \
{\[SIGMA0 (\$[ABCDEFGH])\]} \
$::sha2::SHA256Transform_body \
{((((\1<<30) | ((\1>>2) \& (0x7FFFFFFF>>1))) \& 0xFFFFFFFF) \
^ (((\1<<19) | ((\1>>13) \& (0x7FFFFFFF>>12))) \& 0xFFFFFFFF) \
^ (((\1<<10) | ((\1>>22) \& (0x7FFFFFFF>>21))) \& 0xFFFFFFFF) \
)} \
::sha2::SHA256Transform_body
# Inline the SIGMA1 function
regsub -all -line \
{\[SIGMA1 (\$[ABCDEFGH])\]} \
$::sha2::SHA256Transform_body \
{((((\1<<26) | ((\1>>6) \& (0x7FFFFFFF>>5))) \& 0xFFFFFFFF) \
^ (((\1<<21) | ((\1>>11) \& (0x7FFFFFFF>>10))) \& 0xFFFFFFFF) \
^ (((\1<<7) | ((\1>>25) \& (0x7FFFFFFF>>24))) \& 0xFFFFFFFF) \
)} \
::sha2::SHA256Transform_body
proc ::sha2::SHA256Transform {token msg} $::sha2::SHA256Transform_body
# -------------------------------------------------------------------------
# Convert a integer value into a binary string in big-endian order.
proc ::sha2::byte {n v} {expr {((0xFF << (8 * $n)) & $v) >> (8 * $n)}}
proc ::sha2::bytes {v} {
#format %c%c%c%c [byte 3 $v] [byte 2 $v] [byte 1 $v] [byte 0 $v]
format %c%c%c%c \
[expr {((0xFF000000 & $v) >> 24) & 0xFF}] \
[expr {(0xFF0000 & $v) >> 16}] \
[expr {(0xFF00 & $v) >> 8}] \
[expr {0xFF & $v}]
}
# -------------------------------------------------------------------------
proc ::sha2::Hex {data} {
binary scan $data H* result
return $result
}
# -------------------------------------------------------------------------
# Description:
# Pop the nth element off a list. Used in options processing.
#
proc ::sha2::Pop {varname {nth 0}} {
upvar $varname args
set r [lindex $args $nth]
set args [lreplace $args $nth $nth]
return $r
}
# -------------------------------------------------------------------------
# fileevent handler for chunked file hashing.
#
proc ::sha2::Chunk {token channel {chunksize 4096}} {
upvar #0 $token state
if {[eof $channel]} {
fileevent $channel readable {}
set state(reading) 0
}
SHA256Update $token [read $channel $chunksize]
}
# -------------------------------------------------------------------------
proc ::sha2::_sha256 {ver args} {
array set opts {-hex 0 -filename {} -channel {} -chunksize 4096}
if {[llength $args] == 1} {
set opts(-hex) 1
} else {
while {[string match -* [set option [lindex $args 0]]]} {
switch -glob -- $option {
-hex { set opts(-hex) 1 }
-bin { set opts(-hex) 0 }
-file* { set opts(-filename) [Pop args 1] }
-channel { set opts(-channel) [Pop args 1] }
-chunksize { set opts(-chunksize) [Pop args 1] }
default {
if {[llength $args] == 1} { break }
if {[string compare $option "--"] == 0} { Pop args; break }
set err [join [lsort [concat -bin [array names opts]]] ", "]
return -code error "bad option $option:\
must be one of $err"
}
}
Pop args
}
}
if {$opts(-filename) != {}} {
set opts(-channel) [open $opts(-filename) r]
fconfigure $opts(-channel) -translation binary
}
if {$opts(-channel) == {}} {
if {[llength $args] != 1} {
return -code error "wrong # args: should be\
\"[namespace current]::sha$ver ?-hex|-bin? -filename file\
| -channel channel | string\""
}
set tok [SHA${ver}Init]
SHA${ver}Update $tok [lindex $args 0]
set r [SHA${ver}Final $tok]
} else {
set tok [SHA${ver}Init]
# FRINK: nocheck
set [subst $tok](reading) 1
fileevent $opts(-channel) readable \
[list [namespace origin Chunk] \
$tok $opts(-channel) $opts(-chunksize)]
# FRINK: nocheck
vwait [subst $tok](reading)
set r [SHA${ver}Final $tok]
# If we opened the channel - we should close it too.
if {$opts(-filename) != {}} {
close $opts(-channel)
}
}
if {$opts(-hex)} {
set r [Hex $r]
}
return $r
}
interp alias {} ::sha2::sha256 {} ::sha2::_sha256 256
interp alias {} ::sha2::sha224 {} ::sha2::_sha256 224
# -------------------------------------------------------------------------
proc ::sha2::hmac {args} {
array set opts {-hex 1 -filename {} -channel {} -chunksize 4096}
if {[llength $args] != 2} {
while {[string match -* [set option [lindex $args 0]]]} {
switch -glob -- $option {
-key { set opts(-key) [Pop args 1] }
-hex { set opts(-hex) 1 }
-bin { set opts(-hex) 0 }
-file* { set opts(-filename) [Pop args 1] }
-channel { set opts(-channel) [Pop args 1] }
-chunksize { set opts(-chunksize) [Pop args 1] }
default {
if {[llength $args] == 1} { break }
if {[string compare $option "--"] == 0} { Pop args; break }
set err [join [lsort [array names opts]] ", "]
return -code error "bad option $option:\
must be one of $err"
}
}
Pop args
}
}
if {[llength $args] == 2} {
set opts(-key) [Pop args]
}
if {![info exists opts(-key)]} {
return -code error "wrong # args:\
should be \"hmac ?-hex? -key key -filename file | string\""
}
if {$opts(-filename) != {}} {
set opts(-channel) [open $opts(-filename) r]
fconfigure $opts(-channel) -translation binary
}
if {$opts(-channel) == {}} {
if {[llength $args] != 1} {
return -code error "wrong # args:\
should be \"hmac ?-hex? -key key -filename file | string\""
}
set tok [HMACInit $opts(-key)]
HMACUpdate $tok [lindex $args 0]
set r [HMACFinal $tok]
} else {
set tok [HMACInit $opts(-key)]
# FRINK: nocheck
set [subst $tok](reading) 1
fileevent $opts(-channel) readable \
[list [namespace origin Chunk] \
$tok $opts(-channel) $opts(-chunksize)]
# FRINK: nocheck
vwait [subst $tok](reading)
set r [HMACFinal $tok]
# If we opened the channel - we should close it too.
if {$opts(-filename) != {}} {
close $opts(-channel)
}
}
if {$opts(-hex)} {
set r [Hex $r]
}
return $r
}
# -------------------------------------------------------------------------
package provide sha256 $::sha2::version
# -------------------------------------------------------------------------
# Local Variables:
# mode: tcl
# indent-tabs-mode: nil
# End:

29
sources/twofish/LICENSE.txt Executable file
View File

@ -0,0 +1,29 @@
Copyright (c) 2005, Frank Pilhofer
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
Neither the name of the package's author nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"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 COPYRIGHT
OWNER 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.

3
sources/twofish/pkgIndex.tcl Executable file
View File

@ -0,0 +1,3 @@
package ifneeded itwofish 0.2 [list source [file join $dir twofish.tcl]]

598
sources/twofish/twofish.tcl Executable file
View File

@ -0,0 +1,598 @@
package require Tcl 8.4
package require Itcl
namespace eval ::itwofish {
}
catch {
itcl::delete class ::itwofish::itwofish
}
itcl::class ::itwofish::itwofish {
#
# Constants
#
private common SK_STEP 0x02020202
private common SK_BUMP 0x01010101
private common SK_ROTL 9
private common BLOCK_SIZE 128
private common BLOCK_WORDS [expr {$BLOCK_SIZE / 32}]
private common MAX_KEY_BITS 256
private common MIN_KEY_BITS 128
private common MAX_KEY_WORDS [expr {$MAX_KEY_BITS / 32}]
private common INPUT_WHITEN 0
private common OUTPUT_WHITEN [expr {$INPUT_WHITEN + $BLOCK_SIZE / 32}]
private common ROUND_SUBKEYS [expr {$OUTPUT_WHITEN + $BLOCK_SIZE / 32}]
private common P8x80 {
0xA9 0x67 0xB3 0xE8 0x04 0xFD 0xA3 0x76
0x9A 0x92 0x80 0x78 0xE4 0xDD 0xD1 0x38
0x0D 0xC6 0x35 0x98 0x18 0xF7 0xEC 0x6C
0x43 0x75 0x37 0x26 0xFA 0x13 0x94 0x48
0xF2 0xD0 0x8B 0x30 0x84 0x54 0xDF 0x23
0x19 0x5B 0x3D 0x59 0xF3 0xAE 0xA2 0x82
0x63 0x01 0x83 0x2E 0xD9 0x51 0x9B 0x7C
0xA6 0xEB 0xA5 0xBE 0x16 0x0C 0xE3 0x61
0xC0 0x8C 0x3A 0xF5 0x73 0x2C 0x25 0x0B
0xBB 0x4E 0x89 0x6B 0x53 0x6A 0xB4 0xF1
0xE1 0xE6 0xBD 0x45 0xE2 0xF4 0xB6 0x66
0xCC 0x95 0x03 0x56 0xD4 0x1C 0x1E 0xD7
0xFB 0xC3 0x8E 0xB5 0xE9 0xCF 0xBF 0xBA
0xEA 0x77 0x39 0xAF 0x33 0xC9 0x62 0x71
0x81 0x79 0x09 0xAD 0x24 0xCD 0xF9 0xD8
0xE5 0xC5 0xB9 0x4D 0x44 0x08 0x86 0xE7
0xA1 0x1D 0xAA 0xED 0x06 0x70 0xB2 0xD2
0x41 0x7B 0xA0 0x11 0x31 0xC2 0x27 0x90
0x20 0xF6 0x60 0xFF 0x96 0x5C 0xB1 0xAB
0x9E 0x9C 0x52 0x1B 0x5F 0x93 0x0A 0xEF
0x91 0x85 0x49 0xEE 0x2D 0x4F 0x8F 0x3B
0x47 0x87 0x6D 0x46 0xD6 0x3E 0x69 0x64
0x2A 0xCE 0xCB 0x2F 0xFC 0x97 0x05 0x7A
0xAC 0x7F 0xD5 0x1A 0x4B 0x0E 0xA7 0x5A
0x28 0x14 0x3F 0x29 0x88 0x3C 0x4C 0x02
0xB8 0xDA 0xB0 0x17 0x55 0x1F 0x8A 0x7D
0x57 0xC7 0x8D 0x74 0xB7 0xC4 0x9F 0x72
0x7E 0x15 0x22 0x12 0x58 0x07 0x99 0x34
0x6E 0x50 0xDE 0x68 0x65 0xBC 0xDB 0xF8
0xC8 0xA8 0x2B 0x40 0xDC 0xFE 0x32 0xA4
0xCA 0x10 0x21 0xF0 0xD3 0x5D 0x0F 0x00
0x6F 0x9D 0x36 0x42 0x4A 0x5E 0xC1 0xE0
}
private common P8x81 {
0x75 0xF3 0xC6 0xF4 0xDB 0x7B 0xFB 0xC8
0x4A 0xD3 0xE6 0x6B 0x45 0x7D 0xE8 0x4B
0xD6 0x32 0xD8 0xFD 0x37 0x71 0xF1 0xE1
0x30 0x0F 0xF8 0x1B 0x87 0xFA 0x06 0x3F
0x5E 0xBA 0xAE 0x5B 0x8A 0x00 0xBC 0x9D
0x6D 0xC1 0xB1 0x0E 0x80 0x5D 0xD2 0xD5
0xA0 0x84 0x07 0x14 0xB5 0x90 0x2C 0xA3
0xB2 0x73 0x4C 0x54 0x92 0x74 0x36 0x51
0x38 0xB0 0xBD 0x5A 0xFC 0x60 0x62 0x96
0x6C 0x42 0xF7 0x10 0x7C 0x28 0x27 0x8C
0x13 0x95 0x9C 0xC7 0x24 0x46 0x3B 0x70
0xCA 0xE3 0x85 0xCB 0x11 0xD0 0x93 0xB8
0xA6 0x83 0x20 0xFF 0x9F 0x77 0xC3 0xCC
0x03 0x6F 0x08 0xBF 0x40 0xE7 0x2B 0xE2
0x79 0x0C 0xAA 0x82 0x41 0x3A 0xEA 0xB9
0xE4 0x9A 0xA4 0x97 0x7E 0xDA 0x7A 0x17
0x66 0x94 0xA1 0x1D 0x3D 0xF0 0xDE 0xB3
0x0B 0x72 0xA7 0x1C 0xEF 0xD1 0x53 0x3E
0x8F 0x33 0x26 0x5F 0xEC 0x76 0x2A 0x49
0x81 0x88 0xEE 0x21 0xC4 0x1A 0xEB 0xD9
0xC5 0x39 0x99 0xCD 0xAD 0x31 0x8B 0x01
0x18 0x23 0xDD 0x1F 0x4E 0x2D 0xF9 0x48
0x4F 0xF2 0x65 0x8E 0x78 0x5C 0x58 0x19
0x8D 0xE5 0x98 0x57 0x67 0x7F 0x05 0x64
0xAF 0x63 0xB6 0xFE 0xF5 0xB7 0x3C 0xA5
0xCE 0xE9 0x68 0x44 0xE0 0x4D 0x43 0x69
0x29 0x2E 0xAC 0x15 0x59 0xA8 0x0A 0x9E
0x6E 0x47 0xDF 0x34 0x35 0x6A 0xCF 0xDC
0x22 0xC9 0xC0 0x9B 0x89 0xD4 0xED 0xAB
0x12 0xA2 0x0D 0x52 0xBB 0x02 0x2F 0xA9
0xD7 0x61 0x1E 0xB4 0x50 0x04 0xF6 0xC2
0x16 0x25 0x86 0x56 0x55 0x09 0xBE 0x91
}
#
# Functions
#
private common MDS_GF_FDBK 0x169
public proc f32 {x k32 keyLen} {
set b0 [expr {$x & 255}]
set b1 [expr {($x >> 8) & 255}]
set b2 [expr {($x >> 16) & 255}]
set b3 [expr {($x >> 24) & 255}]
set kl [expr {(($keyLen + 63) / 64) & 3}]
if {$kl == 0} {
set k323 [lindex $k32 3]
set b0 [expr {[lindex $P8x81 $b0] ^ ($k323 & 255)}]
set b1 [expr {[lindex $P8x80 $b1] ^ (($k323 >> 8) & 255)}]
set b2 [expr {[lindex $P8x80 $b2] ^ (($k323 >> 16) & 255)}]
set b3 [expr {[lindex $P8x81 $b3] ^ (($k323 >> 24) & 255)}]
}
if {$kl == 0 || $kl == 3} {
set k322 [lindex $k32 2]
set b0 [expr {[lindex $P8x81 $b0] ^ ($k322 & 255)}]
set b1 [expr {[lindex $P8x81 $b1] ^ (($k322 >> 8) & 255)}]
set b2 [expr {[lindex $P8x80 $b2] ^ (($k322 >> 16) & 255)}]
set b3 [expr {[lindex $P8x80 $b3] ^ (($k322 >> 24) & 255)}]
}
if {$kl == 0 || $kl == 3 || $kl == 2} {
set k320 [lindex $k32 0]
set k321 [lindex $k32 1]
set t0 [expr {[lindex $P8x80 $b0] ^ ($k321 & 255)}]
set t1 [expr {[lindex $P8x81 $b1] ^ (($k321 >> 8) & 255)}]
set t2 [expr {[lindex $P8x80 $b2] ^ (($k321 >> 16) & 255)}]
set t3 [expr {[lindex $P8x81 $b3] ^ (($k321 >> 24) & 255)}]
set t0 [expr {[lindex $P8x80 $t0] ^ ($k320 & 255)}]
set t1 [expr {[lindex $P8x80 $t1] ^ (($k320 >> 8) & 255)}]
set t2 [expr {[lindex $P8x81 $t2] ^ (($k320 >> 16) & 255)}]
set t3 [expr {[lindex $P8x81 $t3] ^ (($k320 >> 24) & 255)}]
set b0 [lindex $P8x81 $t0]
set b1 [lindex $P8x80 $t1]
set b2 [lindex $P8x81 $t2]
set b3 [lindex $P8x80 $t3]
}
#
# MDS_GF_FDBK/2 == 180
# MDS_GF_FDBK/4 == 90
#
set b0x [expr {$b0 ^ (($b0 >> 2) ^ (($b0 & 2) ? 180 : 0)) ^ (($b0 & 1) ? 90 : 0)}]
set b0y [expr {$b0x ^ (($b0 >> 1) ^ (($b0 & 1) ? 180 : 0))}]
set b1x [expr {$b1 ^ (($b1 >> 2) ^ (($b1 & 2) ? 180 : 0)) ^ (($b1 & 1) ? 90 : 0)}]
set b1y [expr {$b1x ^ (($b1 >> 1) ^ (($b1 & 1) ? 180 : 0))}]
set b2x [expr {$b2 ^ (($b2 >> 2) ^ (($b2 & 2) ? 180 : 0)) ^ (($b2 & 1) ? 90 : 0)}]
set b2y [expr {$b2x ^ (($b2 >> 1) ^ (($b2 & 1) ? 180 : 0))}]
set b3x [expr {$b3 ^ (($b3 >> 2) ^ (($b3 & 2) ? 180 : 0)) ^ (($b3 & 1) ? 90 : 0)}]
set b3y [expr {$b3x ^ (($b3 >> 1) ^ (($b3 & 1) ? 180 : 0))}]
return [expr {((($b0 ^ $b1y ^ $b2x ^ $b3x) | \
(($b0x ^ $b1y ^ $b2y ^ $b3) << 8) | \
(($b0y ^ $b1x ^ $b2 ^ $b3y) << 16) | \
(($b0y ^ $b1 ^ $b2y ^ $b3x) << 24)) + 0x100000000) % \
0x100000000}]
}
private common RS_GF_FDBK 0x14d
public proc RS_rem {x} {
variable RS_GF_FDBK
set b [expr {$x >> 24}]
set r [expr {$x & 0x00ffffff}]
set g2 [expr {(($b << 1) ^ (($b & 0x80) ? $RS_GF_FDBK : 0)) & 255}]
set g3 [expr {($b >> 1) ^ (($b & 1) ? ($RS_GF_FDBK >> 1) : 0) ^ $g2}]
return [expr {($r << 8) ^ ($g3 << 24) ^ ($g2 << 16) ^ ($g3 << 8) ^ $b}]
}
public proc RS_MDS_Encode {k0 k1} {
set r $k1
set r [RS_rem $r]
set r [RS_rem $r]
set r [RS_rem $r]
set r [RS_rem $r]
set r [expr {$r ^ $k0}]
set r [RS_rem $r]
set r [RS_rem $r]
set r [RS_rem $r]
set r [RS_rem $r]
return $r
}
#
# Variables
#
public variable keyLen
public variable key32
public variable sboxKeys
public variable subKeys
#
# Initialize with key
#
constructor {key_} {
makeKey $key_
}
public method makeKey {key_} {
set kl [string length $key_]
#
# Key must be at least 128 bits
#
if {$kl < 16} {
set padLen [expr {15-$kl}]
set padding "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
append key_ [string range $padding 0 $padLen]
incr kl $padLen
incr kl
}
#
# Key must be a multiple of 8 bytes
#
if {($kl % 8) != 0} {
set padLen [expr {7-($kl%8)}]
set padding "\x00\x00\x00\x00\x00\x00\x00"
append message [string range $padding 0 $padLen]
incr kl $padLen
incr kl
}
set keyLen [expr {$kl * 8}]
if {$keyLen > $MAX_KEY_BITS} {
error "invalid key length $keyLen"
}
binary scan $key_ i* key32s
set key32 [list]
foreach k $key32s {
lappend key32 [expr {($k + 0x100000000) % 0x100000000}]
}
while {[llength $key32] < $MAX_KEY_WORDS} {
lappend key32 0
}
reKey
}
public method reKey {} {
set subkeyCnt [expr {$ROUND_SUBKEYS + 32}]
set k64Cnt [expr {($keyLen + 63) / 64}]
set sboxKeys [list]
for {set i 0} {$i < $k64Cnt} {incr i} {
set ke [lindex $key32 [expr {2*$i}]]
set ko [lindex $key32 [expr {2*$i+1}]]
set sk [RS_MDS_Encode $ke $ko]
lappend k32e $ke
lappend k32o $ko
set sboxKeys [linsert $sboxKeys 0 $sk]
}
set subkeyCntDiv2 [expr {$subkeyCnt / 2}]
for {set i 0} {$i < $subkeyCntDiv2} {incr i} {
set A [f32 [expr {$i * $SK_STEP}] $k32e $keyLen]
set B [f32 [expr {$i * $SK_STEP + $SK_BUMP}] $k32o $keyLen]
set B [expr {(($B << 8) % 0x100000000) | ($B >> 24)}]
set Ap2B [expr {($A + 2*$B) % 0x100000000}]
lappend subKeys [expr {($A + $B) % 0x100000000}]
lappend subKeys [expr {(($Ap2B << $SK_ROTL) % 0x100000000) | ($Ap2B >> (32 - $SK_ROTL))}]
}
}
#
# Encryption
#
public method intEncrypt {x0 x1 x2 x3} {
#
# INPUT_WHITEN == 0..3
#
set x0 [expr {$x0 ^ [lindex $subKeys 0]}]
set x1 [expr {$x1 ^ [lindex $subKeys 1]}]
set x2 [expr {$x2 ^ [lindex $subKeys 2]}]
set x3 [expr {$x3 ^ [lindex $subKeys 3]}]
set skie $ROUND_SUBKEYS
set skio [expr {$ROUND_SUBKEYS + 1}]
for {set r 0} {$r < 16} {incr r} {
set t0 [f32 $x0 $sboxKeys $keyLen]
set t1 [f32 [expr {(($x1 << 8) % 0x100000000) | ($x1 >> 24)}] $sboxKeys $keyLen]
set x2 [expr {$x2 ^ (($t0 + $t1 + [lindex $subKeys $skie]) % 0x100000000)}]
set x2 [expr {(($x2 << 31) % 0x100000000) | ($x2 >> 1)}]
set x3 [expr {(($x3 << 1) % 0x100000000) | ($x3 >> 31)}]
set x3 [expr {$x3 ^ (($t0 + 2*$t1 + [lindex $subKeys $skio]) % 0x100000000)}]
if {$r < 15} {
set tmp $x0
set x0 $x2
set x2 $tmp
set tmp $x1
set x1 $x3
set x3 $tmp
incr skie 2
incr skio 2
}
}
#
# OUTPUT_WHITEN==4..7
#
set o0 [expr {$x0 ^ [lindex $subKeys 4]}]
set o1 [expr {$x1 ^ [lindex $subKeys 5]}]
set o2 [expr {$x2 ^ [lindex $subKeys 6]}]
set o3 [expr {$x3 ^ [lindex $subKeys 7]}]
return [list $o0 $o1 $o2 $o3]
}
#
# Decryption
#
protected method intDecrypt {x0 x1 x2 x3} {
#
# OUTPUT_WHITEN==4..7
#
set x0 [expr {$x0 ^ [lindex $subKeys 4]}]
set x1 [expr {$x1 ^ [lindex $subKeys 5]}]
set x2 [expr {$x2 ^ [lindex $subKeys 6]}]
set x3 [expr {$x3 ^ [lindex $subKeys 7]}]
set skie [expr {$ROUND_SUBKEYS + 30}]
set skio [expr {$ROUND_SUBKEYS + 31}]
for {set r 16} {$r > 0} {incr r -1} {
set t0 [f32 $x0 $sboxKeys $keyLen]
set t1 [f32 [expr {(($x1 << 8) % 0x100000000) | ($x1 >> 24)}] $sboxKeys $keyLen]
set x2 [expr {(($x2 << 1) % 0x100000000) | ($x2 >> 31)}]
set x2 [expr {$x2 ^ (($t0 + $t1 + [lindex $subKeys $skie]) % 0x100000000)}]
set x3 [expr {$x3 ^ (($t0 + 2*$t1 + [lindex $subKeys $skio]) % 0x100000000)}]
set x3 [expr {(($x3 << 31) % 0x100000000) | ($x3 >> 1)}]
if {$r > 1} {
set tmp $x0
set x0 $x2
set x2 $tmp
set tmp $x1
set x1 $x3
set x3 $tmp
incr skie -2
incr skio -2
}
}
#
# INPUT_WHITEN==0..3
#
set o0 [expr {$x0 ^ [lindex $subKeys 0]}]
set o1 [expr {$x1 ^ [lindex $subKeys 1]}]
set o2 [expr {$x2 ^ [lindex $subKeys 2]}]
set o3 [expr {$x3 ^ [lindex $subKeys 3]}]
return [list $o0 $o1 $o2 $o3]
}
}
#
# Twofish - Electronic Codebook Mode.
#
catch {
itcl::delete class itwofish::ecb
}
itcl::class itwofish::ecb {
inherit itwofish::itwofish
constructor {key} {
itwofish::itwofish::constructor $key
} {
}
#
# Encrypt a 128 bit (16 octet) message block
#
public method encryptBlock {block} {
if {[binary scan $block iiii x0 x1 x2 x3] != 4} {
error "block must be 16 bytes"
}
set x0 [expr {($x0 + 0x100000000) % 0x100000000}]
set x1 [expr {($x1 + 0x100000000) % 0x100000000}]
set x2 [expr {($x2 + 0x100000000) % 0x100000000}]
set x3 [expr {($x3 + 0x100000000) % 0x100000000}]
set d [intEncrypt $x0 $x1 $x2 $x3]
return [binary format i4 $d]
}
#
# Decrypt a 128 bit (16 octet) message block
#
public method decryptBlock {block} {
if {[binary scan $block iiii x0 x1 x2 x3] != 4} {
error "block must be 16 bytes"
}
set x0 [expr {($x0 + 0x100000000) % 0x100000000}]
set x1 [expr {($x1 + 0x100000000) % 0x100000000}]
set x2 [expr {($x2 + 0x100000000) % 0x100000000}]
set x3 [expr {($x3 + 0x100000000) % 0x100000000}]
set d [intDecrypt $x0 $x1 $x2 $x3]
return [binary format i4 $d]
}
}
#
# Twofish - Cipher Block Chaining
#
# Encrypt or decrypt a message. The object is initialized with the
# password and a salt (also known as the Initialization Vector). The
# salt must be exactly 16 bytes long; it is usually chosen at random.
# The encrypted message is of the same length as the cleartext message,
# but padded to the next multiple of 16 bytes.
#
# To decrypt a message, initialize the object with the same password
# and the same salt that were used for encryption. A decrypted message
# has the same (padded) length as the encrypted message. Protocols
# usually embed information about the message length, so that the
# decrypted message can be properly truncated to the length of the
# original cleartext message.
#
# The object can be used to encrypt/decrypt a stream, by calling the
# encrypt or decrypt method repeatedly, passing subsequent blocks of
# the stream.
#
# When encrypting a stream, all blocks but the last must be a multiple
# of 16 bytes in length.
#
# When decrypting a stream, all blocks, including the last, must be
# a multiple of 16 bytes in length. Again, truncation may be necessary
# if the original cleartext stream was not a multiple of 16 bytes in
# length.
#
# Note that the salt changes over time (i.e., it is updated after 16
# bytes each). To encrypt a new message with the same salt, or to
# switch between encryption and decryption, re-configure the "salt"
# variable, i.e.,
#
# set o [itwofish::cbc #auto MyPassword 0123456789abcdef]
# set cipher [$o encrypt "Hello World"]
# $o configure -salt 0123456789abcdef
# set clear [$o decrypt $cipher] ;# Hello World (plus padding)
# itcl::delete object $o
#
catch {
itcl::delete class itwofish::cbc
}
itcl::class itwofish::cbc {
inherit itwofish::itwofish
public variable salt
constructor {key salt_} {
itwofish::itwofish::constructor $key
} {
set salt $salt_
if {[binary scan $salt iiii s0 s1 s2 s3] != 4} {
error "salt must be 16 bytes"
}
}
public method encrypt {message} {
if {[binary scan $salt iiii s0 s1 s2 s3] != 4} {
error "salt must be 16 bytes"
}
set mlen [string length $message]
if {($mlen % 16) != 0} {
set padLen [expr {15-($mlen%16)}]
set padding "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
append message [string range $padding 0 $padLen]
incr mlen $padLen
incr mlen
}
set result ""
for {set i 0} {$i < $mlen} {incr i 16} {
if {[binary scan $message @[set i]iiii x0 x1 x2 x3] != 4} {
error "oops"
}
set x0 [expr {(($x0 ^ $s0) + 0x100000000) % 0x100000000}]
set x1 [expr {(($x1 ^ $s1) + 0x100000000) % 0x100000000}]
set x2 [expr {(($x2 ^ $s2) + 0x100000000) % 0x100000000}]
set x3 [expr {(($x3 ^ $s3) + 0x100000000) % 0x100000000}]
set d [intEncrypt $x0 $x1 $x2 $x3]
set s0 [lindex $d 0]
set s1 [lindex $d 1]
set s2 [lindex $d 2]
set s3 [lindex $d 3]
append result [binary format i4 $d]
}
set salt [binary format iiii $s0 $s1 $s2 $s3]
return $result
}
public method decrypt {message} {
if {[binary scan $salt iiii s0 s1 s2 s3] != 4} {
error "salt must be 16 bytes"
}
set mlen [string length $message]
if {($mlen % 16) != 0} {
error "message must be a multiple of 16 bytes"
}
set result ""
for {set i 0} {$i < $mlen} {incr i 16} {
if {[binary scan $message @[set i]iiii x0 x1 x2 x3] != 4} {
error "oops"
}
set x0 [expr {($x0 + 0x100000000) % 0x100000000}]
set x1 [expr {($x1 + 0x100000000) % 0x100000000}]
set x2 [expr {($x2 + 0x100000000) % 0x100000000}]
set x3 [expr {($x3 + 0x100000000) % 0x100000000}]
set d [intDecrypt $x0 $x1 $x2 $x3]
set d0 [lindex $d 0]
set d1 [lindex $d 1]
set d2 [lindex $d 2]
set d3 [lindex $d 3]
set c0 [expr {(($d0 ^ $s0) + 0x100000000) % 0x100000000}]
set c1 [expr {(($d1 ^ $s1) + 0x100000000) % 0x100000000}]
set c2 [expr {(($d2 ^ $s2) + 0x100000000) % 0x100000000}]
set c3 [expr {(($d3 ^ $s3) + 0x100000000) % 0x100000000}]
set s0 $x0
set s1 $x1
set s2 $x2
set s3 $x3
append result [binary format iiii $c0 $c1 $c2 $c3]
}
set salt [binary format iiii $s0 $s1 $s2 $s3]
return $result
}
}
package provide itwofish 0.2

267
sources/twofish/twotest.tcl Executable file
View File

@ -0,0 +1,267 @@
#! /bin/sh
# the next line restarts using tclsh \
exec tclsh84 "`cygpath --windows $0`" ${1+"$@"}
if {[file exists twofish.tcl]} {
source twofish.tcl
}
package require Itcl
package require Tcl 8.4
package require tcltest
package require itwofish
proc h2b {hex} {
return [binary format H* $hex]
}
proc b2h {bin} {
binary scan $bin H* dummy
return $dummy
}
#
# Twofish ECB test vectors, from http://www.schneier.com/code/ecb_ival.txt
# key bytes, clear bytes, cipher bytes
#
set testVectors {
00000000000000000000000000000000 00000000000000000000000000000000 9F589F5CF6122C32B6BFEC2F2AE8C35A
0123456789ABCDEFFEDCBA98765432100011223344556677 00000000000000000000000000000000 CFD1D2E5A9BE9CDF501F13B892BD2248
0123456789ABCDEFFEDCBA987654321000112233445566778899AABBCCDDEEFF 00000000000000000000000000000000 37527BE0052334B89F0CFCCAE87CFA20
00000000000000000000000000000000 00000000000000000000000000000000 9F589F5CF6122C32B6BFEC2F2AE8C35A
00000000000000000000000000000000 9F589F5CF6122C32B6BFEC2F2AE8C35A D491DB16E7B1C39E86CB086B789F5419
9F589F5CF6122C32B6BFEC2F2AE8C35A D491DB16E7B1C39E86CB086B789F5419 019F9809DE1711858FAAC3A3BA20FBC3
D491DB16E7B1C39E86CB086B789F5419 019F9809DE1711858FAAC3A3BA20FBC3 6363977DE839486297E661C6C9D668EB
019F9809DE1711858FAAC3A3BA20FBC3 6363977DE839486297E661C6C9D668EB 816D5BD0FAE35342BF2A7412C246F752
6363977DE839486297E661C6C9D668EB 816D5BD0FAE35342BF2A7412C246F752 5449ECA008FF5921155F598AF4CED4D0
816D5BD0FAE35342BF2A7412C246F752 5449ECA008FF5921155F598AF4CED4D0 6600522E97AEB3094ED5F92AFCBCDD10
5449ECA008FF5921155F598AF4CED4D0 6600522E97AEB3094ED5F92AFCBCDD10 34C8A5FB2D3D08A170D120AC6D26DBFA
6600522E97AEB3094ED5F92AFCBCDD10 34C8A5FB2D3D08A170D120AC6D26DBFA 28530B358C1B42EF277DE6D4407FC591
34C8A5FB2D3D08A170D120AC6D26DBFA 28530B358C1B42EF277DE6D4407FC591 8A8AB983310ED78C8C0ECDE030B8DCA4
000000000000000000000000000000000000000000000000 00000000000000000000000000000000 EFA71F788965BD4453F860178FC19101
000000000000000000000000000000000000000000000000 EFA71F788965BD4453F860178FC19101 88B2B2706B105E36B446BB6D731A1E88
EFA71F788965BD4453F860178FC191010000000000000000 88B2B2706B105E36B446BB6D731A1E88 39DA69D6BA4997D585B6DC073CA341B2
88B2B2706B105E36B446BB6D731A1E88EFA71F788965BD44 39DA69D6BA4997D585B6DC073CA341B2 182B02D81497EA45F9DAACDC29193A65
39DA69D6BA4997D585B6DC073CA341B288B2B2706B105E36 182B02D81497EA45F9DAACDC29193A65 7AFF7A70CA2FF28AC31DD8AE5DAAAB63
182B02D81497EA45F9DAACDC29193A6539DA69D6BA4997D5 7AFF7A70CA2FF28AC31DD8AE5DAAAB63 D1079B789F666649B6BD7D1629F1F77E
7AFF7A70CA2FF28AC31DD8AE5DAAAB63182B02D81497EA45 D1079B789F666649B6BD7D1629F1F77E 3AF6F7CE5BD35EF18BEC6FA787AB506B
D1079B789F666649B6BD7D1629F1F77E7AFF7A70CA2FF28A 3AF6F7CE5BD35EF18BEC6FA787AB506B AE8109BFDA85C1F2C5038B34ED691BFF
3AF6F7CE5BD35EF18BEC6FA787AB506BD1079B789F666649 AE8109BFDA85C1F2C5038B34ED691BFF 893FD67B98C550073571BD631263FC78
AE8109BFDA85C1F2C5038B34ED691BFF3AF6F7CE5BD35EF1 893FD67B98C550073571BD631263FC78 16434FC9C8841A63D58700B5578E8F67
0000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000 57FF739D4DC92C1BD7FC01700CC8216F
0000000000000000000000000000000000000000000000000000000000000000 57FF739D4DC92C1BD7FC01700CC8216F D43BB7556EA32E46F2A282B7D45B4E0D
57FF739D4DC92C1BD7FC01700CC8216F00000000000000000000000000000000 D43BB7556EA32E46F2A282B7D45B4E0D 90AFE91BB288544F2C32DC239B2635E6
D43BB7556EA32E46F2A282B7D45B4E0D57FF739D4DC92C1BD7FC01700CC8216F 90AFE91BB288544F2C32DC239B2635E6 6CB4561C40BF0A9705931CB6D408E7FA
90AFE91BB288544F2C32DC239B2635E6D43BB7556EA32E46F2A282B7D45B4E0D 6CB4561C40BF0A9705931CB6D408E7FA 3059D6D61753B958D92F4781C8640E58
6CB4561C40BF0A9705931CB6D408E7FA90AFE91BB288544F2C32DC239B2635E6 3059D6D61753B958D92F4781C8640E58 E69465770505D7F80EF68CA38AB3A3D6
3059D6D61753B958D92F4781C8640E586CB4561C40BF0A9705931CB6D408E7FA E69465770505D7F80EF68CA38AB3A3D6 5AB67A5F8539A4A5FD9F0373BA463466
E69465770505D7F80EF68CA38AB3A3D63059D6D61753B958D92F4781C8640E58 5AB67A5F8539A4A5FD9F0373BA463466 DC096BCD99FC72F79936D4C748E75AF7
5AB67A5F8539A4A5FD9F0373BA463466E69465770505D7F80EF68CA38AB3A3D6 DC096BCD99FC72F79936D4C748E75AF7 C5A3E7CEE0F1B7260528A68FB4EA05F2
DC096BCD99FC72F79936D4C748E75AF75AB67A5F8539A4A5FD9F0373BA463466 C5A3E7CEE0F1B7260528A68FB4EA05F2 43D5CEC327B24AB90AD34A79D0469151
}
set testNum 0
set passed 0
set failed 0
if {[llength $argv] == 0} {
foreach {key clear cipher} $testVectors {
incr testNum
set engine [itwofish::ecb \#auto [h2b $key]]
set encrypted [$engine encryptBlock [h2b $clear]]
set decrypted [$engine decryptBlock $encrypted]
itcl::delete object $engine
if {![string equal -nocase $cipher [b2h $encrypted]]} {
puts "varkey-$testNum: encryption failed: [b2h $encrypted] != $cipher"
incr failed
} elseif {![string equal -nocase $clear [b2h $decrypted]]} {
puts "varkey-$testNum: decryption failed: [b2h $decrypted] != $clear"
incr failed
} else {
puts "varkey-$testNum: passed"
incr passed
}
}
exit 0
}
#
# Test the implementation akainst the known-answer tests published on
# Bruce Schneier's Website.
#
set filename [lindex $argv 0]
set file [open $filename]
#
# Expected input:
#
# I=<nn> - test number
# KEY=... - key to use, in hex
# PT=... - plain text, in hex
# CT=... - cipher text, in hex
# IV=... - initialization vector, for CBC mode
#
set keysize 0
set cbcenc 0
set goti 0
set gotiv 0
set gotmc 0
while {![eof $file]} {
set line [string trim [gets $file]]
switch -glob -- $line {
"Monte Carlo Test" {
#
# The mctInner constant that was used in tst2fish.c to generate
# the file that we are testing against. The reference code
# sets this to 100, but the published files seem like they
# were generated with a setting of 10000.
#
set mctInner 10000
set gotmc 1
}
"*CBC*ENCRYPTION*" {
#
# The CBC tests are not reversible, i.e., we can not decrypt
# the text that was encrypted, or vice versa. Therefore, the
# direction diven in the file's header is significant.
#
set cbcenc 1
}
KEYSIZE=* {
set keysize [string range $line 8 end]
}
I=* {
set i [string range $line 2 end]
set goti 1
}
KEY=* {
set key [string range $line 4 end]
}
IV=* {
set iv [string range $line 3 end]
set gotiv 1
}
PT=* {
set pt [string range $line 3 end]
}
CT=* {
set ct [string range $line 3 end]
}
}
if {$line eq "" && $goti} {
# run test
incr testNum
set testname "$filename-$keysize-$testNum"
if {$gotiv && $gotmc} {
#
# Monte Carlo Test, CBC mode
#
set engine [itwofish::cbc \#auto [h2b $key] [h2b $iv]]
if {$cbcenc} {
set tpt [h2b $pt]
set tct [h2b $iv]
for {set i 0} {$i < $mctInner} {incr i} {
set ctPrev $tct
set tct [$engine encrypt $tpt]
set tpt $ctPrev
}
set encrypted $tct
set decrypted [h2b $pt]
} else {
set tct [h2b $ct]
for {set i 0} {$i < $mctInner} {incr i} {
set tct [$engine decrypt $tct]
}
set encrypted [h2b $ct]
set decrypted $tct
}
itcl::delete object $engine
} elseif {$gotiv} {
#
# CBC mode test
#
set engine [itwofish::cbc \#auto [h2b $key] [h2b $iv]]
set encrypted [$engine encrypt [h2b $pt]]
$engine configure -salt [h2b $iv]
set decrypted [$engine decrypt $encrypted]
itcl::delete object $engine
} elseif {$gotmc} {
#
# Monte Carlo Test, ECB mode
#
set engine [itwofish::ecb \#auto [h2b $key]]
set data [h2b $pt]
for {set i 0} {$i < $mctInner} {incr i} {
set data [$engine encryptBlock $data]
}
set encrypted $data
for {set i 0} {$i < $mctInner} {incr i} {
set data [$engine decryptBlock $data]
}
set decrypted $data
itcl::delete object $engine
} else {
#
# ECB mode test
#
set engine [itwofish::ecb \#auto [h2b $key]]
set encrypted [$engine encryptBlock [h2b $pt]]
set decrypted [$engine decryptBlock $encrypted]
itcl::delete object $engine
}
if {![string equal -nocase $ct [b2h $encrypted]]} {
# encrypted text does not match expectation
puts "$testname: encryption failed: [b2h $encrypted] != $ct"
incr failed
} elseif {![string equal -nocase $pt [b2h $decrypted]]} {
# decrypted text does not match original plaintext
puts "$testname: decryption failed: [b2h $decrypted] != $pt"
incr failed
} else {
puts "$testname: passed"
incr passed
}
set goti 0
set gotiv 0
}
}
if {0} {
cd /Frank/soft/gorilla/twofish
source itwofish.tcl
set t [itwofish::itwofish \#auto [h2b 0123456789ABCDEFFEDCBA9876543210]]
set e [$t encryptBlock 0123456789abcdef]
set d [$t decryptBlock $e]
}

BIN
starkit/gorilla15alpha.6.aqua Executable file

Binary file not shown.

BIN
starkit/gorilla15alpha.bin Executable file

Binary file not shown.

Binary file not shown.

BIN
starkit/gorilla15alpha.exe Executable file

Binary file not shown.