Starting Point
After Width: | Height: | Size: 28 KiB |
|
@ -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.
|
|
@ -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.
|
|
@ -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.
|
|
@ -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
|
|
@ -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."
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
package ifneeded iblowfish 0.2 [list source [file join $dir blowfish.tcl]]
|
||||
|
|
@ -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
|
|
@ -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 ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
After Width: | Height: | Size: 113 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 9.9 KiB |
|
@ -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.
|
|
@ -0,0 +1,2 @@
|
|||
|
||||
package ifneeded pwsafe 0.2 [list source [file join $dir pwsafe.tcl]]
|
|
@ -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]
|
||||
}
|
||||
}
|
|
@ -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\]\]"
|
||||
}
|
||||
}
|
||||
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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
|
|
@ -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
|
|
@ -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.
|
|
@ -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]]
|
|
@ -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:
|
||||
|
||||
|
|
@ -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.
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
package ifneeded itwofish 0.2 [list source [file join $dir twofish.tcl]]
|
||||
|
|
@ -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
|
||||
|
|
@ -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]
|
||||
}
|
||||
|