diff --git a/src/KP2AKdbLibraryBinding/Additions/AboutAdditions.txt b/src/KP2AKdbLibraryBinding/Additions/AboutAdditions.txt new file mode 100644 index 00000000..08caee33 --- /dev/null +++ b/src/KP2AKdbLibraryBinding/Additions/AboutAdditions.txt @@ -0,0 +1,48 @@ +Additions allow you to add arbitrary C# to the generated classes +before they are compiled. This can be helpful for providing convenience +methods or adding pure C# classes. + +== Adding Methods to Generated Classes == + +Let's say the library being bound has a Rectangle class with a constructor +that takes an x and y position, and a width and length size. It will look like +this: + +public partial class Rectangle +{ + public Rectangle (int x, int y, int width, int height) + { + // JNI bindings + } +} + +Imagine we want to add a constructor to this class that takes a Point and +Size structure instead of 4 ints. We can add a new file called Rectangle.cs +with a partial class containing our new method: + +public partial class Rectangle +{ + public Rectangle (Point location, Size size) : + this (location.X, location.Y, size.Width, size.Height) + { + } +} + +At compile time, the additions class will be added to the generated class +and the final assembly will a Rectangle class with both constructors. + + +== Adding C# Classes == + +Another thing that can be done is adding fully C# managed classes to the +generated library. In the above example, let's assume that there isn't a +Point class available in Java or our library. The one we create doesn't need +to interact with Java, so we'll create it like a normal class in C#. + +By adding a Point.cs file with this class, it will end up in the binding library: + +public class Point +{ + public int X { get; set; } + public int Y { get; set; } +} \ No newline at end of file diff --git a/src/KP2AKdbLibraryBinding/Jars/AboutJars.txt b/src/KP2AKdbLibraryBinding/Jars/AboutJars.txt new file mode 100644 index 00000000..c359b62f --- /dev/null +++ b/src/KP2AKdbLibraryBinding/Jars/AboutJars.txt @@ -0,0 +1,24 @@ +This directory is for Android .jars. + +There are 2 types of jars that are supported: + +== Input Jar == + +This is the jar that bindings should be generated for. + +For example, if you were binding the Google Maps library, this would +be Google's "maps.jar". + +Set the build action for these jars in the properties page to "InputJar". + + +== Reference Jars == + +These are jars that are referenced by the input jar. C# bindings will +not be created for these jars. These jars will be used to resolve +types used by the input jar. + +NOTE: Do not add "android.jar" as a reference jar. It will be added automatically +based on the Target Framework selected. + +Set the build action for these jars in the properties page to "ReferenceJar". \ No newline at end of file diff --git a/src/KP2AKdbLibraryBinding/KP2AKdbLibraryBinding.csproj b/src/KP2AKdbLibraryBinding/KP2AKdbLibraryBinding.csproj new file mode 100644 index 00000000..3c41dfc6 --- /dev/null +++ b/src/KP2AKdbLibraryBinding/KP2AKdbLibraryBinding.csproj @@ -0,0 +1,69 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {70D3844A-D9FA-4A64-B205-A84C6A822196} + {10368E6C-D01B-4462-8E8B-01FC667A7035};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + Properties + KP2AKdbLibraryBinding + KP2AKdbLibraryBinding + 512 + v2.2 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + Designer + + + + + + + Jars\kp2akdblibrary.jar + + + + + \ No newline at end of file diff --git a/src/KP2AKdbLibraryBinding/Properties/AssemblyInfo.cs b/src/KP2AKdbLibraryBinding/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..8cf53d6a --- /dev/null +++ b/src/KP2AKdbLibraryBinding/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Android.App; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("KP2AKdbLibraryBinding")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("KP2AKdbLibraryBinding")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] + +// Add some common permissions, these can be removed if not needed +[assembly: UsesPermission(Android.Manifest.Permission.Internet)] +[assembly: UsesPermission(Android.Manifest.Permission.WriteExternalStorage)] diff --git a/src/KP2AKdbLibraryBinding/Transforms/EnumFields.xml b/src/KP2AKdbLibraryBinding/Transforms/EnumFields.xml new file mode 100644 index 00000000..22959957 --- /dev/null +++ b/src/KP2AKdbLibraryBinding/Transforms/EnumFields.xml @@ -0,0 +1,14 @@ + + + \ No newline at end of file diff --git a/src/KP2AKdbLibraryBinding/Transforms/EnumMethods.xml b/src/KP2AKdbLibraryBinding/Transforms/EnumMethods.xml new file mode 100644 index 00000000..49216c61 --- /dev/null +++ b/src/KP2AKdbLibraryBinding/Transforms/EnumMethods.xml @@ -0,0 +1,13 @@ + + + \ No newline at end of file diff --git a/src/KP2AKdbLibraryBinding/Transforms/Metadata.xml b/src/KP2AKdbLibraryBinding/Transforms/Metadata.xml new file mode 100644 index 00000000..0dd04e93 --- /dev/null +++ b/src/KP2AKdbLibraryBinding/Transforms/Metadata.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/KP2AKdbLibraryBinding/libs/armeabi-v7a/libfinal-key.so b/src/KP2AKdbLibraryBinding/libs/armeabi-v7a/libfinal-key.so new file mode 100644 index 00000000..6564d096 Binary files /dev/null and b/src/KP2AKdbLibraryBinding/libs/armeabi-v7a/libfinal-key.so differ diff --git a/src/KP2AKdbLibraryBinding/libs/armeabi/libfinal-key.so b/src/KP2AKdbLibraryBinding/libs/armeabi/libfinal-key.so new file mode 100644 index 00000000..8ce832a1 Binary files /dev/null and b/src/KP2AKdbLibraryBinding/libs/armeabi/libfinal-key.so differ diff --git a/src/KP2AKdbLibraryBinding/libs/mips/libfinal-key.so b/src/KP2AKdbLibraryBinding/libs/mips/libfinal-key.so new file mode 100644 index 00000000..1c4391b8 Binary files /dev/null and b/src/KP2AKdbLibraryBinding/libs/mips/libfinal-key.so differ diff --git a/src/KeePass.sln b/src/KeePass.sln index 90f0816b..06741647 100644 --- a/src/KeePass.sln +++ b/src/KeePass.sln @@ -23,6 +23,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AppCompatV7Binding", "AppCo EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AndroidFileChooserBinding", "AndroidFileChooserBinding\AndroidFileChooserBinding.csproj", "{3C0F7FE5-639F-4422-A087-8B26CF862D1B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KP2AKdbLibraryBinding", "KP2AKdbLibraryBinding\KP2AKdbLibraryBinding.csproj", "{70D3844A-D9FA-4A64-B205-A84C6A822196}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -160,12 +162,12 @@ Global {A5F8FB02-00E0-4335-91EF-AEAA2C2F3C48}.Release|Mixed Platforms.Build.0 = Release|Any CPU {A5F8FB02-00E0-4335-91EF-AEAA2C2F3C48}.Release|Win32.ActiveCfg = Release|Any CPU {A5F8FB02-00E0-4335-91EF-AEAA2C2F3C48}.Release|x64.ActiveCfg = Release|Any CPU - {A5F8FB02-00E0-4335-91EF-AEAA2C2F3C48}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU - {A5F8FB02-00E0-4335-91EF-AEAA2C2F3C48}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU + {A5F8FB02-00E0-4335-91EF-AEAA2C2F3C48}.ReleaseNoNet|Any CPU.ActiveCfg = ReleaseNoNet|Any CPU + {A5F8FB02-00E0-4335-91EF-AEAA2C2F3C48}.ReleaseNoNet|Any CPU.Build.0 = ReleaseNoNet|Any CPU {A5F8FB02-00E0-4335-91EF-AEAA2C2F3C48}.ReleaseNoNet|Mixed Platforms.ActiveCfg = ReleaseNoNet|Any CPU {A5F8FB02-00E0-4335-91EF-AEAA2C2F3C48}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU - {A5F8FB02-00E0-4335-91EF-AEAA2C2F3C48}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU - {A5F8FB02-00E0-4335-91EF-AEAA2C2F3C48}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU + {A5F8FB02-00E0-4335-91EF-AEAA2C2F3C48}.ReleaseNoNet|Win32.ActiveCfg = ReleaseNoNet|Any CPU + {A5F8FB02-00E0-4335-91EF-AEAA2C2F3C48}.ReleaseNoNet|x64.ActiveCfg = ReleaseNoNet|Any CPU {46B769B8-2C58-4138-9CC0-70E3AE3C9A3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {46B769B8-2C58-4138-9CC0-70E3AE3C9A3A}.Debug|Any CPU.Build.0 = Debug|Any CPU {46B769B8-2C58-4138-9CC0-70E3AE3C9A3A}.Debug|Any CPU.Deploy.0 = Debug|Any CPU @@ -262,6 +264,24 @@ Global {3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Mixed Platforms.Build.0 = ReleaseNoNet|Any CPU {3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU {3C0F7FE5-639F-4422-A087-8B26CF862D1B}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU + {70D3844A-D9FA-4A64-B205-A84C6A822196}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {70D3844A-D9FA-4A64-B205-A84C6A822196}.Debug|Any CPU.Build.0 = Debug|Any CPU + {70D3844A-D9FA-4A64-B205-A84C6A822196}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {70D3844A-D9FA-4A64-B205-A84C6A822196}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {70D3844A-D9FA-4A64-B205-A84C6A822196}.Debug|Win32.ActiveCfg = Debug|Any CPU + {70D3844A-D9FA-4A64-B205-A84C6A822196}.Debug|x64.ActiveCfg = Debug|Any CPU + {70D3844A-D9FA-4A64-B205-A84C6A822196}.Release|Any CPU.ActiveCfg = Release|Any CPU + {70D3844A-D9FA-4A64-B205-A84C6A822196}.Release|Any CPU.Build.0 = Release|Any CPU + {70D3844A-D9FA-4A64-B205-A84C6A822196}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {70D3844A-D9FA-4A64-B205-A84C6A822196}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {70D3844A-D9FA-4A64-B205-A84C6A822196}.Release|Win32.ActiveCfg = Release|Any CPU + {70D3844A-D9FA-4A64-B205-A84C6A822196}.Release|x64.ActiveCfg = Release|Any CPU + {70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Any CPU.ActiveCfg = Release|Any CPU + {70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Any CPU.Build.0 = Release|Any CPU + {70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Mixed Platforms.ActiveCfg = Release|Any CPU + {70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Mixed Platforms.Build.0 = Release|Any CPU + {70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|Win32.ActiveCfg = Release|Any CPU + {70D3844A-D9FA-4A64-B205-A84C6A822196}.ReleaseNoNet|x64.ActiveCfg = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/KeePassLib2Android/PwDatabase.cs b/src/KeePassLib2Android/PwDatabase.cs index 09827039..876d44d4 100644 --- a/src/KeePassLib2Android/PwDatabase.cs +++ b/src/KeePassLib2Android/PwDatabase.cs @@ -564,22 +564,20 @@ namespace KeePassLib /// IO connection to load the database from. /// sKey used to open the specified database. /// Logger, which gets all status messages. + /// public void Open(IOConnectionInfo ioSource, CompositeKey pwKey, - IStatusLogger slLogger) + IStatusLogger slLogger, IDatabaseLoader loader) { Open(IOConnection.OpenRead(ioSource), UrlUtil.StripExtension( - UrlUtil.GetFileName(ioSource.Path)), ioSource, pwKey, slLogger ); + UrlUtil.GetFileName(ioSource.Path)), ioSource, pwKey, slLogger , loader); } /// - /// Open a database. The URL may point to any supported data source. + /// Open a database provided as Stream. /// - /// IO connection to load the database from. - /// Key used to open the specified database. - /// Logger, which gets all status messages. public void Open(Stream s, string fileNameWithoutPathAndExt, IOConnectionInfo ioSource, CompositeKey pwKey, - IStatusLogger slLogger) + IStatusLogger slLogger, IDatabaseLoader loader) { Debug.Assert(s != null); if (s == null) throw new ArgumentNullException("s"); @@ -602,14 +600,10 @@ namespace KeePassLib m_bModified = false; - KdbxFile kdbx = new KdbxFile(this); - kdbx.DetachBinaries = m_strDetachBins; - - kdbx.Load(s, KdbpFile.GetFormatToUse(ioSource), slLogger); - s.Close(); - - m_pbHashOfLastIO = kdbx.HashOfFileOnDisk; - m_pbHashOfFileOnDisk = kdbx.HashOfFileOnDisk; + loader.PopulateDatabaseFromStream(this, pwKey, s, slLogger); + + m_pbHashOfLastIO = loader.HashOfLastStream; + m_pbHashOfFileOnDisk = loader.HashOfLastStream; Debug.Assert(m_pbHashOfFileOnDisk != null); m_bDatabaseOpened = true; diff --git a/src/java/KP2AKdbLibrary/.classpath b/src/java/KP2AKdbLibrary/.classpath new file mode 100644 index 00000000..51769745 --- /dev/null +++ b/src/java/KP2AKdbLibrary/.classpath @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/java/KP2AKdbLibrary/.gitignore b/src/java/KP2AKdbLibrary/.gitignore new file mode 100644 index 00000000..eb8ccedb --- /dev/null +++ b/src/java/KP2AKdbLibrary/.gitignore @@ -0,0 +1,5 @@ +build.properties +local.properties +bin +gen +obj diff --git a/src/java/KP2AKdbLibrary/.project b/src/java/KP2AKdbLibrary/.project new file mode 100644 index 00000000..3da51bd9 --- /dev/null +++ b/src/java/KP2AKdbLibrary/.project @@ -0,0 +1,33 @@ + + + KP2AKdbLibrary + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/src/java/KP2AKdbLibrary/.settings/org.eclipse.jdt.core.prefs b/src/java/KP2AKdbLibrary/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..b080d2dd --- /dev/null +++ b/src/java/KP2AKdbLibrary/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/src/java/KP2AKdbLibrary/AndroidManifest.xml b/src/java/KP2AKdbLibrary/AndroidManifest.xml new file mode 100644 index 00000000..d2e540ed --- /dev/null +++ b/src/java/KP2AKdbLibrary/AndroidManifest.xml @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file diff --git a/src/java/KP2AKdbLibrary/COPYING.gpl-2.0 b/src/java/KP2AKdbLibrary/COPYING.gpl-2.0 new file mode 100644 index 00000000..d511905c --- /dev/null +++ b/src/java/KP2AKdbLibrary/COPYING.gpl-2.0 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + 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 Lesser 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 + + 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. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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) year 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. + + , 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 Lesser General +Public License instead of this License. diff --git a/src/java/KP2AKdbLibrary/LICENSE b/src/java/KP2AKdbLibrary/LICENSE new file mode 100644 index 00000000..6a72a87c --- /dev/null +++ b/src/java/KP2AKdbLibrary/LICENSE @@ -0,0 +1,306 @@ +--- + +The KeePass icon was created by Francis Jacquerye and is licensed under the terms of the GPLv2 or GPLv3. + + +--- + +Files under src/org/apache/* + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +--- + +Files under src/org/bouncycaste1x/*: + + Copyright (c) 2000 - 2008 The Legion Of The Bouncy Castle + (http://www.bouncycastle.org) + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + +--- + +Files under src/org/phoneid/*: + + KeePass for J2ME + + Copyright 2007 Naomaru Itoi + + This file was derived from + + Java clone of KeePass - A KeePass file viewer for Java + Copyright 2006 Bill Zwicky + + 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; version 2 + + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +--- + +Files under src/com/android/keepass/* : + +Copyright 2009 Brian Pellin . + + 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, see . + +--- + +Files under jni/openssl-0.98l/*: + +/* ==================================================================== + * Copyright (c) 1998-2008 The OpenSSL Project. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. All advertising materials mentioning features or use of this + * software must display the following acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit. (http://www.openssl.org/)" + * + * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to + * endorse or promote products derived from this software without + * prior written permission. For written permission, please contact + * openssl-core@openssl.org. + * + * 5. Products derived from this software may not be called "OpenSSL" + * nor may "OpenSSL" appear in their names without prior written + * permission of the OpenSSL Project. + * + * 6. Redistributions of any form whatsoever must retain the following + * acknowledgment: + * "This product includes software developed by the OpenSSL Project + * for use in the OpenSSL Toolkit (http://www.openssl.org/)" + * + * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY + * EXPRESSED 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 OpenSSL PROJECT OR + * ITS 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. + * ==================================================================== + * + * This product includes cryptographic software written by Eric Young + * (eay@cryptsoft.com). This product includes software written by Tim + * Hudson (tjh@cryptsoft.com). + * + */ + +--- + +Files under biz.source_code.base64Coder + +// Copyright 2003-2010 Christian d'Heureuse, Inventec Informatik AG, Zurich, Switzerland +// www.source-code.biz, www.inventec.ch/chdh +// +// This module is multi-licensed and may be used under the terms +// of any of the following licenses: +// +// EPL, Eclipse Public License, http://www.eclipse.org/legal +// LGPL, GNU Lesser General Public License, http://www.gnu.org/licenses/lgpl.html +// AL, Apache License, http://www.apache.org/licenses +// BSD, BSD License, http://www.opensource.org/licenses/bsd-license.php +// +// Please contact the author if you need another license. +// This module is provided "as is", without warranties of any kind. + +--- + +Files ic00.png to ic61.png under res/drawable, res/drawable-hdpi and res/drawable-ldpi + +TITLE: NUVOLA ICON THEME for KDE 3.x +AUTHOR: David Vignoni | ICON KING +SITE: http://www.icon-king.com +MAILING LIST: http://mail.icon-king.com/mailman/listinfo/nuvola_icon-king.com + +Copyright (c) 2003-2004 David Vignoni. + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation, +version 2.1 of the License. +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. +You should have received a copy of the GNU Lesser General Public +License along with this library (see the the license.txt file); if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +#######**** NOTE THIS ADD-ON ****####### +The GNU Lesser General Public License or LGPL is written for software libraries +in the first place. The LGPL has to be considered valid for this artwork +library too. +Nuvola icon theme for KDE 3.x is a special kind of software library, it is an +artwork library, it's elements can be used in a Graphical User Interface, or +GUI. +Source code, for this library means: + - raster png image* . +The LGPL in some sections obliges you to make the files carry +notices. With images this is in some cases impossible or hardly usefull. +With this library a notice is placed at a prominent place in the directory +containing the elements. You may follow this practice. +The exception in section 6 of the GNU Lesser General Public License covers +the use of elements of this art library in a GUI. +dave [at] icon-king.com + +Date: 15 october 2004 +Version: 1.0 + +DESCRIPTION: + +Icon theme for KDE 3.x. +Icons where designed using Adobe Illustrator, and then exported to PNG format. +Icons shadows and minor corrections were done using Adobe Photoshop. +Kiconedit was used to correct some 16x16 and 22x22 icons. + +LICENSE + +Released under GNU Lesser General Public License (LGPL) +Look at the license.txt file. + +CONTACT + +David Vignoni +e-mail : david [at] icon-king.com +ICQ : 117761009 +http: http://www.icon-king.com + +--- + +Files ic62.png under res/drawable, res/drawable-hdpi and res/drawable-ldpi + +Based on http://de.wikipedia.org/w/index.php?title=Datei:Tux.svg&filetimestamp=20090927073505 + +The copyright holder of this file allows anyone to use it for any purpose, +provided that the copyright holders Larry Ewing, Simon Budig and +Anja Gerwinski are mentioned. + +--- + +Files ic63.png under res/drawable, res/drawable-hdpi and res/drawable-ldpi + +Based on http://en.wikipedia.org/wiki/File:ASF-logo.svg + +Apache logo +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. You may +obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0. +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations under +the License. + +--- + +Files ic64.png under res/drawable, res/drawable-hdpi and res/drawable-ldpi + +!!! Not included yet !!! + +--- + +Files ic65.png, ic67.png and ic68.png under res/drawable, res/drawable-hdpi and res/drawable-ldpi + +Created by Tobias Selig and licensed under the terms of the GPLv2 or GPLv3. + +--- + +File ic66.png under under res/drawable, res/drawable-hdpi and res/drawable-ldpi + +Based on http://commons.wikimedia.org/wiki/File:Dollar_symbol_gold.svg + +Author: Rugby471 + +Permission is granted to copy, distribute and/or modify this document under +the terms of the GNU Free Documentation License, Version 1.2 or any later +version published by the Free Software Foundation; with no Invariant Sections, +no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is +included in the section entitled "GNU Free Documentation License". + +--- + +Many files under jni/final_key/ are coverage by the following license: + + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + diff --git a/src/java/KP2AKdbLibrary/README b/src/java/KP2AKdbLibrary/README new file mode 100644 index 00000000..ab5f669e --- /dev/null +++ b/src/java/KP2AKdbLibrary/README @@ -0,0 +1 @@ +Created by extracting parts of Keepassdroid \ No newline at end of file diff --git a/src/java/KP2AKdbLibrary/jni/.gitignore b/src/java/KP2AKdbLibrary/jni/.gitignore new file mode 100644 index 00000000..9011a2f1 --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/.gitignore @@ -0,0 +1,3 @@ +openssl-0.9.8l +aes-src-29-04-09.zip +sha2-07-01-07.zip diff --git a/src/java/KP2AKdbLibrary/jni/Android.mk b/src/java/KP2AKdbLibrary/jni/Android.mk new file mode 100644 index 00000000..eabf3354 --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/Android.mk @@ -0,0 +1,3 @@ +# Recursively sources all Android.mk files in subdirs: +include $(call all-subdir-makefiles) + diff --git a/src/java/KP2AKdbLibrary/jni/Application.mk b/src/java/KP2AKdbLibrary/jni/Application.mk new file mode 100644 index 00000000..aff927b8 --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/Application.mk @@ -0,0 +1,3 @@ +APP_MODULES := aes sha final-key +APP_OPTIM := release +APP_ABI := armeabi armeabi-v7a x86 mips diff --git a/src/java/KP2AKdbLibrary/jni/aes/Android.mk b/src/java/KP2AKdbLibrary/jni/aes/Android.mk new file mode 100644 index 00000000..e8d12dae --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/Android.mk @@ -0,0 +1,13 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := aes + +LOCAL_SRC_FILES := \ + aescrypt.c \ + aeskey.c \ + aes_modes.c \ + aestab.c + +include $(BUILD_STATIC_LIBRARY) diff --git a/src/java/KP2AKdbLibrary/jni/aes/aes.h b/src/java/KP2AKdbLibrary/jni/aes/aes.h new file mode 100644 index 00000000..46d1f6bd --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/aes.h @@ -0,0 +1,205 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 20/12/2007 + + This file contains the definitions required to use AES in C. See aesopt.h + for optimisation details. +*/ + +#ifndef _AES_H +#define _AES_H + +#include + +/* This include is used to find 8 & 32 bit unsigned integer types */ +#include "brg_types.h" + +#if defined(__cplusplus) +extern "C" +{ +#endif + +#define AES_128 /* if a fast 128 bit key scheduler is needed */ +#define AES_192 /* if a fast 192 bit key scheduler is needed */ +#define AES_256 /* if a fast 256 bit key scheduler is needed */ +#define AES_VAR /* if variable key size scheduler is needed */ +#define AES_MODES /* if support is needed for modes */ + +/* The following must also be set in assembler files if being used */ + +#define AES_ENCRYPT /* if support for encryption is needed */ +#define AES_DECRYPT /* if support for decryption is needed */ +#define AES_REV_DKS /* define to reverse decryption key schedule */ + +#define AES_BLOCK_SIZE 16 /* the AES block size in bytes */ +#define N_COLS 4 /* the number of columns in the state */ + +/* The key schedule length is 11, 13 or 15 16-byte blocks for 128, */ +/* 192 or 256-bit keys respectively. That is 176, 208 or 240 bytes */ +/* or 44, 52 or 60 32-bit words. */ + +#if defined( AES_VAR ) || defined( AES_256 ) +#define KS_LENGTH 60 +#elif defined( AES_192 ) +#define KS_LENGTH 52 +#else +#define KS_LENGTH 44 +#endif + +#define AES_RETURN INT_RETURN + +/* the character array 'inf' in the following structures is used */ +/* to hold AES context information. This AES code uses cx->inf.b[0] */ +/* to hold the number of rounds multiplied by 16. The other three */ +/* elements can be used by code that implements additional modes */ + +typedef union +{ uint_32t l; + uint_8t b[4]; +} aes_inf; + +typedef struct +{ uint_32t ks[KS_LENGTH]; + aes_inf inf; +} aes_encrypt_ctx; + +typedef struct +{ uint_32t ks[KS_LENGTH]; + aes_inf inf; +} aes_decrypt_ctx; + +/* This routine must be called before first use if non-static */ +/* tables are being used */ + +AES_RETURN aes_init(void); + +/* Key lengths in the range 16 <= key_len <= 32 are given in bytes, */ +/* those in the range 128 <= key_len <= 256 are given in bits */ + +#if defined( AES_ENCRYPT ) + +#if defined( AES_128 ) || defined( AES_VAR) +AES_RETURN aes_encrypt_key128(const unsigned char *key, aes_encrypt_ctx cx[1]); +#endif + +#if defined( AES_192 ) || defined( AES_VAR) +AES_RETURN aes_encrypt_key192(const unsigned char *key, aes_encrypt_ctx cx[1]); +#endif + +#if defined( AES_256 ) || defined( AES_VAR) +AES_RETURN aes_encrypt_key256(const unsigned char *key, aes_encrypt_ctx cx[1]); +#endif + +#if defined( AES_VAR ) +AES_RETURN aes_encrypt_key(const unsigned char *key, int key_len, aes_encrypt_ctx cx[1]); +#endif + +AES_RETURN aes_encrypt(const unsigned char *in, unsigned char *out, const aes_encrypt_ctx cx[1]); + +#endif + +#if defined( AES_DECRYPT ) + +#if defined( AES_128 ) || defined( AES_VAR) +AES_RETURN aes_decrypt_key128(const unsigned char *key, aes_decrypt_ctx cx[1]); +#endif + +#if defined( AES_192 ) || defined( AES_VAR) +AES_RETURN aes_decrypt_key192(const unsigned char *key, aes_decrypt_ctx cx[1]); +#endif + +#if defined( AES_256 ) || defined( AES_VAR) +AES_RETURN aes_decrypt_key256(const unsigned char *key, aes_decrypt_ctx cx[1]); +#endif + +#if defined( AES_VAR ) +AES_RETURN aes_decrypt_key(const unsigned char *key, int key_len, aes_decrypt_ctx cx[1]); +#endif + +AES_RETURN aes_decrypt(const unsigned char *in, unsigned char *out, const aes_decrypt_ctx cx[1]); + +#endif + +#if defined( AES_MODES ) + +/* Multiple calls to the following subroutines for multiple block */ +/* ECB, CBC, CFB, OFB and CTR mode encryption can be used to handle */ +/* long messages incremantally provided that the context AND the iv */ +/* are preserved between all such calls. For the ECB and CBC modes */ +/* each individual call within a series of incremental calls must */ +/* process only full blocks (i.e. len must be a multiple of 16) but */ +/* the CFB, OFB and CTR mode calls can handle multiple incremental */ +/* calls of any length. Each mode is reset when a new AES key is */ +/* set but ECB and CBC operations can be reset without setting a */ +/* new key by setting a new IV value. To reset CFB, OFB and CTR */ +/* without setting the key, aes_mode_reset() must be called and the */ +/* IV must be set. NOTE: All these calls update the IV on exit so */ +/* this has to be reset if a new operation with the same IV as the */ +/* previous one is required (or decryption follows encryption with */ +/* the same IV array). */ + +AES_RETURN aes_test_alignment_detection(unsigned int n); + +AES_RETURN aes_ecb_encrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, const aes_encrypt_ctx cx[1]); + +AES_RETURN aes_ecb_decrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, const aes_decrypt_ctx cx[1]); + +AES_RETURN aes_cbc_encrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, const aes_encrypt_ctx cx[1]); + +AES_RETURN aes_cbc_decrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, const aes_decrypt_ctx cx[1]); + +AES_RETURN aes_mode_reset(aes_encrypt_ctx cx[1]); + +AES_RETURN aes_cfb_encrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, aes_encrypt_ctx cx[1]); + +AES_RETURN aes_cfb_decrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, aes_encrypt_ctx cx[1]); + +#define aes_ofb_encrypt aes_ofb_crypt +#define aes_ofb_decrypt aes_ofb_crypt + +AES_RETURN aes_ofb_crypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, aes_encrypt_ctx cx[1]); + +typedef void cbuf_inc(unsigned char *cbuf); + +#define aes_ctr_encrypt aes_ctr_crypt +#define aes_ctr_decrypt aes_ctr_crypt + +AES_RETURN aes_ctr_crypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *cbuf, cbuf_inc ctr_inc, aes_encrypt_ctx cx[1]); + +#endif + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/src/java/KP2AKdbLibrary/jni/aes/aes.txt b/src/java/KP2AKdbLibrary/jni/aes/aes.txt new file mode 100644 index 00000000..ca165632 --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/aes.txt @@ -0,0 +1,556 @@ + +An AES (Rijndael) Implementation in C/C++ (as specified in FIPS-197) +==================================================================== + +Changes in this Version (16/04/2007) +==================================== + +These changes remove errors in the VC++ build files and add some +improvements in file naming consitency and portability. There are +no changes to overcome reported bugs in the code. + +1. gen_tabs() has been renamed to aes_init() to better decribe its + function to those not familiar with AES internals. + +2. via_ace.h has been renamed to aes_via_ace.h. + +3. Minor changes have been made to aestab.h and aestab.c to enable + all the code to be compiled in either C or C++. + +4. The code for detecting memory alignment in aesmdoes.c has been + simplified and a new routine has been added: + + aes_test_alignment_detection() + + to check that the aligment test is likely to be correct. + +5. The addition of support for Structured Exception Handling (SEH) + to YASM (well done Peter and Michael!) has allowed the AMD64 + x64 assembler code to be changed to comply with SEH requriements. + +6. Corrections to build files (for win32 debug build). + +Overview +======== + +This code implements AES for both 32 and 64 bit systems with optional +assembler support for x86 and AMD64/EM64T (but optimised for AMD64). + +The basic AES source code files are as follows: + +aes.h the header file needed to use AES in C +aescpp.h the header file required with to use AES in C++ +aesopt.h the header file for setting options (and some common code) +aestab.h the header file for the AES table declaration +aescrypt.c the main C source code file for encryption and decryption +aeskey.c the main C source code file for the key schedule +aestab.c the main file for the AES tables +brg_types.h a header defining some standard types and DLL defines +brg_endian.h a header containing code to detect or define endianness +aes_x86_v1.asm x86 assembler (YASM) alternative to aescrypt.c using + large tables +aes_x86_v2.asm x86 assembler (YASM) alternative to aescrypt.c using + compressed tables +aes_amd64.asm AMD64 assembler (YASM) alternative to aescrypt.c using + compressed tables + +In addition AES modes are implemented in the files: + +aes_modes.c AES modes with optional support for VIA ACE detection and use +aes_via_ace.h the header file for VIA ACE support + +Other associated files for testing and support are: + +aesaux.h header for auxilliary routines for testsing +aesaux.c auxilliary routines for testsingt +aestst.h header file for setting the testing environment +rdtsc.h a header file that provides access to the Time Stamp Counter +aestst.c a simple test program for quick tests of the AES code +aesgav.c a program to generate and verify the test vector files +aesrav.c a program to verify output against the test vector files +aestmr.c a program to time the code on x86 systems +modetest.c a program to test the AES modes support +vbxam.doc a demonstration of AES DLL use from Visual Basic in Microsoft Word +vb.txt Visual Basic code from the above example (win32 only) +aesxam.c an example of AES use +tablegen.c a program to generate a simplified 'aestab.c' file for + use with compilers that find aestab.c too complex +yasm.rules the YASM build rules file for Microsoft Visual Studio 2005 +via_ace.txt describes support for the VIA ACE cryptography engine +aes.txt this file + +Building The AES Libraries +-------------------------- + +A. Versions +----------- + +The code can be used to build static and dynamic libraries, each in five +versions: + + C uses C source code only + ASM_X86_V1C large table x86 assembler code for encrypt/decrypt + ASM_X86_V2 compressed table x86 assembler for encrypt/decrypt and keying + ASM_X86_V2C compressed table x86 assembler code for encrypt/decrypt + ASM_AMD64 compressed table x86 assembler code for encrypt/decrypt + +The C version can be compiled for Win32 or x64, the x86 assembler versions +are for Win32 only and the AMD64 version for x64 only. + +B. Types +-------- + +The code makes use of types defined as uint_t where is the length +of the type, for example, the unsigned 32-bit type is 'uint_32t'. These are +NOT the same as the fixed width integer types in C99, inttypes.h and stdint.h +since several attempts to use these types have shown that support for them is +still highly variable. But a regular expression search and replace in VC++ +with search on 'uint_{:z}t' and a replace with 'uint\1_t' will convert these +types to C99 types (there should be similar search/replace facilities on other +systems). + +C. YASM +------- + +If you wish to use the x86 assembler files you will also need the YASM open +source x86 assembler (r1331 or later) for Windows which can be obtained from: + + http://www.tortall.net/projects/yasm/ + +This assembler should be placed in the bin directory used by VC++, which, for +Visual Stduio 2005, is typically: + + C:\Program Files (x86)\Microsoft Visual Studio 8\VC\bin + +You will also need to move the yasm.rules file from this distribution into +the directory where Visual Studio 2005 expects to find it, which is typically: + + C:\Program Files (x86)\Microsoft Visual Studio 8\VC\VCProjectDefaults + +Alternatively you can configure the path for rules files within Visual Studio. + +D. Configuration +---------------- + +The following configurations are available as projects for Visual Studio 2005 +but the following descriptions should allow them to be built in other x86 +environments: + + lib_generic_c Win32 and x64 + headers: aes.h, aesopt.h, aestab.h, brg_endian.h, tdefs,h + C source: aescrypt.c, aeskey.c, aestab.c, aes_modes.c + defines + dll_generic_c Win32 and x64 + headers: aes.h, aesopt.h, aestab.h, brg_endian.h, tdefs,h + C source: aescrypt.c, aeskey.c, aestab.c, aes_modes.c + defines DLL_EXPORT + + lib_asm_x86_v1c Win32 + headers: aes.h, aesopt.h, aestab.h, brg_endian.h, tdefs,h + C source: aeskey.c, aestab.c, aes_modes.c + x86 assembler: aes_x86_v1.asm + defines ASM_X86_V1C (set for C and assembler files) + dll_asm_x86_v1c Win32 + headers: aes.h, aesopt.h, aestab.h, brg_endian.h, tdefs,h + C source: aeskey.c, aestab.c, aes_modes.c + x86 assembler: aes_x86_v1.asm + defines DLL_EXPORT, ASM_X86_V1C (set for C and assembler files) + + lib_asm_x86_v2c Win32 + headers: aes.h, aesopt.h, aestab.h, brg_endian.h, tdefs,h + C source: aeskey.c, aestab.c, aes_modes.c + x86 assembler: aes_x86_v2.asm + defines ASM_X86_V2C (set for C and assembler files) + dll_asm_x86_v2c Win32 + headers: aes.h, aesopt.h, aestab.h, brg_endian.h, tdefs,h + C source: aeskey.c, aestab.c, aes_modes.c + x86 assembler: aes_x86_v1.asm + defines DLL_EXPORT, ASM_X86_V2C (set for C and assembler files) + + lib_asm_x86_v2 Win32 + headers: aes.h, aesopt.h, aestab.h, brg_endian.h, tdefs,h + C source: aes_modes.c + x86 assembler: aes_x86_v1.asm + defines ASM_X86_V2 (set for C and assembler files) + dll_asm_x86_v2 Win32 + headers: aes.h, aesopt.h, aestab.h, brg_endian.h, tdefs,h + C source: aes_modes.c + x86 assembler: aes_x86_v1.asm + defines DLL_EXPORT, ASM_AMD64_C (set for C and assembler files) + + lib_asm_amd64_c x64 + headers: aes.h, aesopt.h, aestab.h, brg_endian.h, tdefs,h + C source: aes_modes.c + x86 assembler: aes_amd64.asm + defines ASM_X86_V2 (set for C and assembler files) + dll_asm_amd64_c x64 + headers: aes.h, aesopt.h, aestab.h, brg_endian.h, tdefs,h + C source: aes_modes.c + x86 assembler: aes_amd64.asm + defines DLL_EXPORT, ASM_AMD64_C (set for C and assembler files) + +Notes: + +ASM_X86_V1C is defined if using the version 1 assembler code (aescrypt1.asm). + The defines in the assember file must match those in aes.h and + aesopt.h). Also remember to include/exclude the right assembler + and C files in the build to avoid undefined or multiply defined + symbols - include aescrypt1.asm and exclude aescrypt.c and + aescrypt2.asm. + +ASM_X86_V2 is defined if using the version 2 assembler code (aescrypt2.asm). + This version provides a full, self contained assembler version + and does not use any C source code files except for the mutiple + block encryption modes that are provided by aes_modes.c. The define + ASM_X86_V2 must be set on the YASM command line (or in aescrypt2.asm) + to use this version and all C files except aec_modes.c and. for the + DLL build, aestab.c must be excluded from the build. + +ASM_X86_V2C is defined when using the version 2 assembler code (aescrypt2.asm) + with faster key scheduling provided by the in C code (the options in + the assember file must match those in aes.h and aesopt.h). In this + case aeskey.c and aestab.c are needed with aescrypt2.asm and the + define ASM_X86_V2C must be set for both the C files and for + asecrypt2.asm command lines (or in aesopt.h and aescrypt2.asm). + Include aescrypt2.asm aeskey.c and aestab.c, exclude aescrypt.c for + this option. + +ASM_AMD64_C is defined when using the AMD64 assembly code because the C key + scheduling is sued in this case. + +DLL_EXPORT must be defined to generate the DLL version of the code and + to run tests on it + +DLL_IMPORT must be defined to use the DLL version of the code in an + application program + +Directories the paths for the various directories for test vector input and + output have to be set in aestst.h + +VIA ACE see the via_ace.txt for this item + +Static The static libraries are named: +Libraries + aes_lib_generic_c.lib + aes_lib_asm_x86_v1c.lib + aes_lib_asm_x86_v2.lib + aes_lib_asm_x86_v2c.lib + aes_lib_asm_amd64_c.lib + + and placed in one of the the directories: + + lib\win32\release\ + lib\win32\debug\ + lib\x64\release\ + lib\x64\debug\ + + in the aes root directory depending on the platform(win32 or + x64) and the build (release or debug). After any of these is + built it is then copied into aes.lib, which is the library + that is subsequently used for testing. Hence testing is for + the last static library built. + +Dynamic The static libraries are named: +Libraries + aes_lib_generic_c.dll + aes_lib_asm_x86_v1c.dll + aes_lib_asm_x86_v2.dll + aes_lib_asm_x86_v2c.dll + aes_lib_asm_amd64_c.dll + + and placed in one of the the directories: + + dll\win32\release\ + dll\win32\debug\ + dll\x64\release\ + dll\x64\debug\ + + in the aes root directory depending on the platform(win32 or + x64) and the build (release or debug). Each DLL library: + + aes_.dll + + has three associated files: + + aes_dll_.lib the library file for implicit linking + aes_dll_.exp the exports file + aes_dll_.pdb the symbol file + + After any DLL is built it and its three related files are then + copied into aes.lib, aes.lib, aes,exp and aes.pdb, which are + the libraries used for testing. Hence testing is for the last + static library or DLL built. + +E. Testing +---------- + +These tests require that the test vector files are placed in the 'testvals' +subdirectory. If the AES Algorithm Validation Suite tests will be use3d then +the *.fax files need to be put in the 'testvals\fax' subdirectory. This is +covered in more detail below. + +The projects test_dll and time_dll are used to test and time the last DLL +built. These use the files: + + test_dll: Win32 (x64 for the C and AMD64 versions) + headers: aes.h, aescpp.h, brg_types.h, aesaux.h and aestst.h + C source: aesaux.c, aesrav.c + defines: DLL_IMPORT + + time_dll: Win32 (x64 for the C and AMD64 versions) + headers: aes.h, aescpp.h, brg_types.h, aesaux.h aestst.h and rdtsc.h + C source: aesaux.c, aestmr.c + defines: DLL_IMPORT + +and link to the DLL using explicit linking. However, if the lib file associated +with the DLL is linked into this project and the symbol DYNAMIC_LINK in aestst.h +is left undefined, then implicit linking will be used + +The projects test_lib and time_lib are used to test and time the last static LIB +built. They use the files: + + test_lib: Win32 (x64 for the C and AMD64 versions) + headers: aes.h, aescpp.h, brg_types.h, aesaux.h and aestst.h + C source: aesaux.c, aesrav.c + defines: + + time_lib: Win32 (x64 for the C and AMD64 versions) + headers: aes.h, aescpp.h, brg_types.h, aesaux.h, aestst.h and rdtsc.h + C source: aesaux.c, aestmr.c + defines: + +and link to the last static library built. + +The above test take command line arguments that determine which test are run +as follows: + + test_lib /t:[knec] /k:[468] + test_dll /t:[knec] /k:[468] + +where the symbols in square brackets can be used in any combination (without +the brackets) and have the following meanings: + + /t:[knec] selects which tests are used + /k:[468] selects the key lengths used + /c compares output with reference (see later) + + k: generate ECB Known Answer Test files + n: generate ECB Known Answer Test files (new) + e: generate ECB Monte Carlo Test files + c: generate CBC Monte Carlo Test files + +and the characters giving the lengths are digits representing the lengths in +32-bit units.\n\n"); + +The project test_modes tests the AES modes. It uses the files: + + test_modes: Win32 or x64 + headers: aes.h, aescpp.h, brg_types.h, aesaux,h and aestst.h + C source: aesaux.c, modetest.c + defines: none for static library test, DLL_IMPORT for DLL test + +which again links to the last library built. + +F. Other Applications +--------------------- + +These are: + + gen_tests builds the test_vector files. The commad line is + gen_tests /t:knec /k:468 /c + as described earlier + + test_aes_avs run the AES Algorithm Validation Suite tests for + ECB, CBC, CFB and OFB modes + + gen_tables builds a simple version of aes_tab.c (in aestab2.c) + for compilers that cannot handle the normal version + aes_example provides an example of AES use + +These applications are linked to the last static library built or, if +DLL_IMPORT is defined during compilation, to the last DLL built. + +G. Use of the VIA ACE Cryptography Engine +----------------------------------------- + +The use of the code with the VIA ACE cryptography engine in described in the +file via_ace.txt. In outline aes_modes.c is used and USE_VIA_ACE_IF_PRESENT +is defined either in section 2 of aesopt.h or as a compilation option in Visual +Studio. If in addition ASSUME_VIA_ACE_PRESENT is also defined then all normal +AES code will be removed if not needed to support VIA ACE use. If VIA ACE +support is needed and AES assembler is being used only the ASM_X86_V1C and +ASM_X86_V2C versions should be used since ASM_X86_V2 and ASM_AMD64 do not +support the VIA ACE engine. + +H. The AES Test Vector Files +---------------------------- + +These files fall in the following groups (where is a two digit +number): + +1. ecbvk.txt ECB vectors with variable key +2. ecbvt.txt ECB vectors with variable text +3. ecbnk.txt new ECB vectors with variable key +4. ecbnt.txt new ECB vectors with variable text +5. ecbme.txt ECB monte carlo encryption test vectors +6. ecbmd.txt ECB monte carlo decryption test vectors +7. cbcme.txt CBC monte carlo encryption test vectors +8. cbcmd.txt CBC monte carlo decryption test vectors + +The first digit of the numeric suffix on the filename gives the block size +in 32 bit units and the second numeric digit gives the key size. For example, +the file ecbvk44.txt provides the test vectors for ECB encryption with a 128 +bit block size and a 128 bit key size. The test routines expect to find these +files in the 'testvals' subdirectory within the aes root directory. The +'outvals' subdirectory is used for outputs that are compared with the files +in 'testvals'. Note that the monte carlo test vectors are the result of +applying AES iteratively 10000 times, not just once. + +The AES Algorithm Validation Suite tests can be run for ECB, CBC, CFB and +OFB modes (CFB1 and CFB8 are not implemented). The test routine uses the +*.fax test files, which should be placed in the 'testvals\fax' subdirectory. + +I. The Basic AES Calling Interface +---------------------------------- + +The basic AES code keeps its state in a context, there being different +contexts for encryption and decryption: + + aes_encrypt_ctx + aes_decrypt_ctx + +The AES code is initialised with the call + + aes_init(void) + +although this is only essential if the option to generate the AES tables at +run-time has been set in the options (i.e.fixed tables are not being used). + +The AES encryption key is set by one of the calls: + + aes_encrypt_key128(const unsigned char *key, aes_encrypt_ctx cx[1]) + aes_encrypt_key192(const unsigned char *key, aes_encrypt_ctx cx[1]) + aes_encrypt_key256(const unsigned char *key, aes_encrypt_ctx cx[1]) + +or by: + + aes_encrypt_key(const unsigned char *key, int key_len, + aes_encrypt_ctx cx[1]) + +where the key length is set by 'key_len', which can be the length in bits +or bytes. + +Similarly, the AES decryption key is set by one of: + + aes_decrypt_key128(const unsigned char *key, aes_decrypt_ctx cx[1]) + aes_decrypt_key192(const unsigned char *key, aes_decrypt_ctx cx[1]) + aes_decrypt_key256(const unsigned char *key, aes_decrypt_ctx cx[1]) + +or by: + + aes_decrypt_key(const unsigned char *key, int key_len, + aes_decrypt_ctx cx[1]) + +Encryption and decryption for a single 16 byte block is then achieved using: + + aes_encrypt(const unsigned char *in, unsigned char *out, + const aes_encrypt_ctx cx[1]) + aes_decrypt(const unsigned char *in, unsigned char *out, + const aes_decrypt_ctx cx[1]) + +The above subroutines return a value of EXIT_SUCCESS or EXIT_FAILURE +depending on whether the operation succeeded or failed. + +J. The Calling Interface for the AES Modes +------------------------------------------ + +The subroutines for the AES modes, ECB, CBC, CFB, OFB and CTR, each process +blocks of variable length and can also be called several times to complete +single mode operations incrementally on long messages (or those messages, +not all of which are available at the same time). The calls: + + aes_ecb_encrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, const aes_encrypt_ctx cx[1]) + + aes_ecb_decrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, const aes_decrypt_ctx cx[1]) + +for ECB operations and those for CBC: + + aes_cbc_encrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, const aes_encrypt_ctx cx[1]) + + aes_cbc_decrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, const aes_decrypt_ctx cx[1]) + +can only process blocks whose lengths are multiples of 16 bytes but the calls +for CFB, OFB and CTR mode operations: + + aes_cfb_encrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, aes_encrypt_ctx cx[1]) + + aes_cfb_decrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, aes_encrypt_ctx cx[1]) + + aes_ofb_encrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, aes_encrypt_ctx cx[1]) + + aes_ofb_decrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, aes_encrypt_ctx cx[1]) + + aes_ctr_encrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *cbuf, cbuf_inc ctr_inc, aes_encrypt_ctx cx[1]) + + aes_ctr_decrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *cbuf, cbuf_inc ctr_inc, aes_encrypt_ctx cx[1]) + +can process blocks of any length. Note also that CFB, OFB and CTR mode calls only +use AES encryption contexts even during decryption operations. + +The calls CTR mode operations use a buffer (cbuf) which holds the counter value +together with a function parameter: + + void cbuf_inc(unsigned char *cbuf); + +that is ued to update the counter value after each 16 byte AES operation. The +counter buffer is updated appropriately to allow for incremental operations. + +Please note the following IMPORTANT points about the AES mode subroutines: + + 1. All modes are reset when a new AES key is set. + + 2. Incremental calls to the different modes cannot + be mixed. If a change of mode is needed a new + key must be set or a reset must be issued (see + below). + + 3. For modes with IVs, the IV value is an inpu AND + an ouput since it is updated after each call to + the value needed for any subsequent incremental + call(s). If the mode is reset, the IV hence has + to be set (or reset) as well. + + 4. ECB operations must be multiples of 16 bytes + but do not need to be reset for new operations. + + 5. CBC operations must also be multiples of 16 + bytes and are reset for a new operation by + setting the IV. + + 6. CFB, OFB and CTR mode must be reset by setting + a new IV value AND by calling: + + aes_mode_reset(aes_encrypt_ctx cx[1]) + + For CTR mode the cbuf value also has to be reset. + + 7. CFB, OFB and CTR modes only use AES encryption + operations and contexts and do not need AES + decrytpion operations. + + 8. AES keys remain valid across resets and changes + of mode (but encryption and decryption keys must + both be set if they are needed). + + Brian Gladman 22/07/2008 + \ No newline at end of file diff --git a/src/java/KP2AKdbLibrary/jni/aes/aes_amd64.asm b/src/java/KP2AKdbLibrary/jni/aes/aes_amd64.asm new file mode 100644 index 00000000..9111f812 --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/aes_amd64.asm @@ -0,0 +1,905 @@ + +; --------------------------------------------------------------------------- +; Copyright (c) 1998-2007, Brian Gladman, Worcester, UK. All rights reserved. +; +; LICENSE TERMS +; +; The free distribution and use of this software is allowed (with or without +; changes) provided that: +; +; 1. source code distributions include the above copyright notice, this +; list of conditions and the following disclaimer; +; +; 2. binary distributions include the above copyright notice, this list +; of conditions and the following disclaimer in their documentation; +; +; 3. the name of the copyright holder is not used to endorse products +; built using this software without specific written permission. +; +; DISCLAIMER +; +; This software is provided 'as is' with no explicit or implied warranties +; in respect of its properties, including, but not limited to, correctness +; and/or fitness for purpose. +; --------------------------------------------------------------------------- +; Issue 20/12/2007 +; +; I am grateful to Dag Arne Osvik for many discussions of the techniques that +; can be used to optimise AES assembler code on AMD64/EM64T architectures. +; Some of the techniques used in this implementation are the result of +; suggestions made by him for which I am most grateful. + +; An AES implementation for AMD64 processors using the YASM assembler. This +; implemetation provides only encryption, decryption and hence requires key +; scheduling support in C. It uses 8k bytes of tables but its encryption and +; decryption performance is very close to that obtained using large tables. +; It can use either Windows or Gnu/Linux calling conventions, which are as +; follows: +; windows gnu/linux +; +; in_blk rcx rdi +; out_blk rdx rsi +; context (cx) r8 rdx +; +; preserved rsi - + rbx, rbp, rsp, r12, r13, r14 & r15 +; registers rdi - on both +; +; destroyed - rsi + rax, rcx, rdx, r8, r9, r10 & r11 +; registers - rdi on both +; +; The default convention is that for windows, the gnu/linux convention being +; used if __GNUC__ is defined. +; +; Define _SEH_ to include support for Win64 structured exception handling +; (this requires YASM version 0.6 or later). +; +; This code provides the standard AES block size (128 bits, 16 bytes) and the +; three standard AES key sizes (128, 192 and 256 bits). It has the same call +; interface as my C implementation. It uses the Microsoft C AMD64 calling +; conventions in which the three parameters are placed in rcx, rdx and r8 +; respectively. The rbx, rsi, rdi, rbp and r12..r15 registers are preserved. +; +; AES_RETURN aes_encrypt(const unsigned char in_blk[], +; unsigned char out_blk[], const aes_encrypt_ctx cx[1]); +; +; AES_RETURN aes_decrypt(const unsigned char in_blk[], +; unsigned char out_blk[], const aes_decrypt_ctx cx[1]); +; +; AES_RETURN aes_encrypt_key(const unsigned char key[], +; const aes_encrypt_ctx cx[1]); +; +; AES_RETURN aes_decrypt_key(const unsigned char key[], +; const aes_decrypt_ctx cx[1]); +; +; AES_RETURN aes_encrypt_key(const unsigned char key[], +; unsigned int len, const aes_decrypt_ctx cx[1]); +; +; AES_RETURN aes_decrypt_key(const unsigned char key[], +; unsigned int len, const aes_decrypt_ctx cx[1]); +; +; where is 128, 102 or 256. In the last two calls the length can be in +; either bits or bytes. +; +; Comment in/out the following lines to obtain the desired subroutines. These +; selections MUST match those in the C header file aes.h + +%define AES_128 ; define if AES with 128 bit keys is needed +%define AES_192 ; define if AES with 192 bit keys is needed +%define AES_256 ; define if AES with 256 bit keys is needed +%define AES_VAR ; define if a variable key size is needed +%define ENCRYPTION ; define if encryption is needed +%define DECRYPTION ; define if decryption is needed +%define AES_REV_DKS ; define if key decryption schedule is reversed + +%define LAST_ROUND_TABLES ; define for the faster version using extra tables + +; The encryption key schedule has the following in memory layout where N is the +; number of rounds (10, 12 or 14): +; +; lo: | input key (round 0) | ; each round is four 32-bit words +; | encryption round 1 | +; | encryption round 2 | +; .... +; | encryption round N-1 | +; hi: | encryption round N | +; +; The decryption key schedule is normally set up so that it has the same +; layout as above by actually reversing the order of the encryption key +; schedule in memory (this happens when AES_REV_DKS is set): +; +; lo: | decryption round 0 | = | encryption round N | +; | decryption round 1 | = INV_MIX_COL[ | encryption round N-1 | ] +; | decryption round 2 | = INV_MIX_COL[ | encryption round N-2 | ] +; .... .... +; | decryption round N-1 | = INV_MIX_COL[ | encryption round 1 | ] +; hi: | decryption round N | = | input key (round 0) | +; +; with rounds except the first and last modified using inv_mix_column() +; But if AES_REV_DKS is NOT set the order of keys is left as it is for +; encryption so that it has to be accessed in reverse when used for +; decryption (although the inverse mix column modifications are done) +; +; lo: | decryption round 0 | = | input key (round 0) | +; | decryption round 1 | = INV_MIX_COL[ | encryption round 1 | ] +; | decryption round 2 | = INV_MIX_COL[ | encryption round 2 | ] +; .... .... +; | decryption round N-1 | = INV_MIX_COL[ | encryption round N-1 | ] +; hi: | decryption round N | = | encryption round N | +; +; This layout is faster when the assembler key scheduling provided here +; is used. +; +; The DLL interface must use the _stdcall convention in which the number +; of bytes of parameter space is added after an @ to the sutine's name. +; We must also remove our parameters from the stack before return (see +; the do_exit macro). Define DLL_EXPORT for the Dynamic Link Library version. + +;%define DLL_EXPORT + +; End of user defines + +%ifdef AES_VAR +%ifndef AES_128 +%define AES_128 +%endif +%ifndef AES_192 +%define AES_192 +%endif +%ifndef AES_256 +%define AES_256 +%endif +%endif + +%ifdef AES_VAR +%define KS_LENGTH 60 +%elifdef AES_256 +%define KS_LENGTH 60 +%elifdef AES_192 +%define KS_LENGTH 52 +%else +%define KS_LENGTH 44 +%endif + +%define r0 rax +%define r1 rdx +%define r2 rcx +%define r3 rbx +%define r4 rsi +%define r5 rdi +%define r6 rbp +%define r7 rsp + +%define raxd eax +%define rdxd edx +%define rcxd ecx +%define rbxd ebx +%define rsid esi +%define rdid edi +%define rbpd ebp +%define rspd esp + +%define raxb al +%define rdxb dl +%define rcxb cl +%define rbxb bl +%define rsib sil +%define rdib dil +%define rbpb bpl +%define rspb spl + +%define r0h ah +%define r1h dh +%define r2h ch +%define r3h bh + +%define r0d eax +%define r1d edx +%define r2d ecx +%define r3d ebx + +; finite field multiplies by {02}, {04} and {08} + +%define f2(x) ((x<<1)^(((x>>7)&1)*0x11b)) +%define f4(x) ((x<<2)^(((x>>6)&1)*0x11b)^(((x>>6)&2)*0x11b)) +%define f8(x) ((x<<3)^(((x>>5)&1)*0x11b)^(((x>>5)&2)*0x11b)^(((x>>5)&4)*0x11b)) + +; finite field multiplies required in table generation + +%define f3(x) (f2(x) ^ x) +%define f9(x) (f8(x) ^ x) +%define fb(x) (f8(x) ^ f2(x) ^ x) +%define fd(x) (f8(x) ^ f4(x) ^ x) +%define fe(x) (f8(x) ^ f4(x) ^ f2(x)) + +; macro for expanding S-box data + +%macro enc_vals 1 + db %1(0x63),%1(0x7c),%1(0x77),%1(0x7b),%1(0xf2),%1(0x6b),%1(0x6f),%1(0xc5) + db %1(0x30),%1(0x01),%1(0x67),%1(0x2b),%1(0xfe),%1(0xd7),%1(0xab),%1(0x76) + db %1(0xca),%1(0x82),%1(0xc9),%1(0x7d),%1(0xfa),%1(0x59),%1(0x47),%1(0xf0) + db %1(0xad),%1(0xd4),%1(0xa2),%1(0xaf),%1(0x9c),%1(0xa4),%1(0x72),%1(0xc0) + db %1(0xb7),%1(0xfd),%1(0x93),%1(0x26),%1(0x36),%1(0x3f),%1(0xf7),%1(0xcc) + db %1(0x34),%1(0xa5),%1(0xe5),%1(0xf1),%1(0x71),%1(0xd8),%1(0x31),%1(0x15) + db %1(0x04),%1(0xc7),%1(0x23),%1(0xc3),%1(0x18),%1(0x96),%1(0x05),%1(0x9a) + db %1(0x07),%1(0x12),%1(0x80),%1(0xe2),%1(0xeb),%1(0x27),%1(0xb2),%1(0x75) + db %1(0x09),%1(0x83),%1(0x2c),%1(0x1a),%1(0x1b),%1(0x6e),%1(0x5a),%1(0xa0) + db %1(0x52),%1(0x3b),%1(0xd6),%1(0xb3),%1(0x29),%1(0xe3),%1(0x2f),%1(0x84) + db %1(0x53),%1(0xd1),%1(0x00),%1(0xed),%1(0x20),%1(0xfc),%1(0xb1),%1(0x5b) + db %1(0x6a),%1(0xcb),%1(0xbe),%1(0x39),%1(0x4a),%1(0x4c),%1(0x58),%1(0xcf) + db %1(0xd0),%1(0xef),%1(0xaa),%1(0xfb),%1(0x43),%1(0x4d),%1(0x33),%1(0x85) + db %1(0x45),%1(0xf9),%1(0x02),%1(0x7f),%1(0x50),%1(0x3c),%1(0x9f),%1(0xa8) + db %1(0x51),%1(0xa3),%1(0x40),%1(0x8f),%1(0x92),%1(0x9d),%1(0x38),%1(0xf5) + db %1(0xbc),%1(0xb6),%1(0xda),%1(0x21),%1(0x10),%1(0xff),%1(0xf3),%1(0xd2) + db %1(0xcd),%1(0x0c),%1(0x13),%1(0xec),%1(0x5f),%1(0x97),%1(0x44),%1(0x17) + db %1(0xc4),%1(0xa7),%1(0x7e),%1(0x3d),%1(0x64),%1(0x5d),%1(0x19),%1(0x73) + db %1(0x60),%1(0x81),%1(0x4f),%1(0xdc),%1(0x22),%1(0x2a),%1(0x90),%1(0x88) + db %1(0x46),%1(0xee),%1(0xb8),%1(0x14),%1(0xde),%1(0x5e),%1(0x0b),%1(0xdb) + db %1(0xe0),%1(0x32),%1(0x3a),%1(0x0a),%1(0x49),%1(0x06),%1(0x24),%1(0x5c) + db %1(0xc2),%1(0xd3),%1(0xac),%1(0x62),%1(0x91),%1(0x95),%1(0xe4),%1(0x79) + db %1(0xe7),%1(0xc8),%1(0x37),%1(0x6d),%1(0x8d),%1(0xd5),%1(0x4e),%1(0xa9) + db %1(0x6c),%1(0x56),%1(0xf4),%1(0xea),%1(0x65),%1(0x7a),%1(0xae),%1(0x08) + db %1(0xba),%1(0x78),%1(0x25),%1(0x2e),%1(0x1c),%1(0xa6),%1(0xb4),%1(0xc6) + db %1(0xe8),%1(0xdd),%1(0x74),%1(0x1f),%1(0x4b),%1(0xbd),%1(0x8b),%1(0x8a) + db %1(0x70),%1(0x3e),%1(0xb5),%1(0x66),%1(0x48),%1(0x03),%1(0xf6),%1(0x0e) + db %1(0x61),%1(0x35),%1(0x57),%1(0xb9),%1(0x86),%1(0xc1),%1(0x1d),%1(0x9e) + db %1(0xe1),%1(0xf8),%1(0x98),%1(0x11),%1(0x69),%1(0xd9),%1(0x8e),%1(0x94) + db %1(0x9b),%1(0x1e),%1(0x87),%1(0xe9),%1(0xce),%1(0x55),%1(0x28),%1(0xdf) + db %1(0x8c),%1(0xa1),%1(0x89),%1(0x0d),%1(0xbf),%1(0xe6),%1(0x42),%1(0x68) + db %1(0x41),%1(0x99),%1(0x2d),%1(0x0f),%1(0xb0),%1(0x54),%1(0xbb),%1(0x16) +%endmacro + +%macro dec_vals 1 + db %1(0x52),%1(0x09),%1(0x6a),%1(0xd5),%1(0x30),%1(0x36),%1(0xa5),%1(0x38) + db %1(0xbf),%1(0x40),%1(0xa3),%1(0x9e),%1(0x81),%1(0xf3),%1(0xd7),%1(0xfb) + db %1(0x7c),%1(0xe3),%1(0x39),%1(0x82),%1(0x9b),%1(0x2f),%1(0xff),%1(0x87) + db %1(0x34),%1(0x8e),%1(0x43),%1(0x44),%1(0xc4),%1(0xde),%1(0xe9),%1(0xcb) + db %1(0x54),%1(0x7b),%1(0x94),%1(0x32),%1(0xa6),%1(0xc2),%1(0x23),%1(0x3d) + db %1(0xee),%1(0x4c),%1(0x95),%1(0x0b),%1(0x42),%1(0xfa),%1(0xc3),%1(0x4e) + db %1(0x08),%1(0x2e),%1(0xa1),%1(0x66),%1(0x28),%1(0xd9),%1(0x24),%1(0xb2) + db %1(0x76),%1(0x5b),%1(0xa2),%1(0x49),%1(0x6d),%1(0x8b),%1(0xd1),%1(0x25) + db %1(0x72),%1(0xf8),%1(0xf6),%1(0x64),%1(0x86),%1(0x68),%1(0x98),%1(0x16) + db %1(0xd4),%1(0xa4),%1(0x5c),%1(0xcc),%1(0x5d),%1(0x65),%1(0xb6),%1(0x92) + db %1(0x6c),%1(0x70),%1(0x48),%1(0x50),%1(0xfd),%1(0xed),%1(0xb9),%1(0xda) + db %1(0x5e),%1(0x15),%1(0x46),%1(0x57),%1(0xa7),%1(0x8d),%1(0x9d),%1(0x84) + db %1(0x90),%1(0xd8),%1(0xab),%1(0x00),%1(0x8c),%1(0xbc),%1(0xd3),%1(0x0a) + db %1(0xf7),%1(0xe4),%1(0x58),%1(0x05),%1(0xb8),%1(0xb3),%1(0x45),%1(0x06) + db %1(0xd0),%1(0x2c),%1(0x1e),%1(0x8f),%1(0xca),%1(0x3f),%1(0x0f),%1(0x02) + db %1(0xc1),%1(0xaf),%1(0xbd),%1(0x03),%1(0x01),%1(0x13),%1(0x8a),%1(0x6b) + db %1(0x3a),%1(0x91),%1(0x11),%1(0x41),%1(0x4f),%1(0x67),%1(0xdc),%1(0xea) + db %1(0x97),%1(0xf2),%1(0xcf),%1(0xce),%1(0xf0),%1(0xb4),%1(0xe6),%1(0x73) + db %1(0x96),%1(0xac),%1(0x74),%1(0x22),%1(0xe7),%1(0xad),%1(0x35),%1(0x85) + db %1(0xe2),%1(0xf9),%1(0x37),%1(0xe8),%1(0x1c),%1(0x75),%1(0xdf),%1(0x6e) + db %1(0x47),%1(0xf1),%1(0x1a),%1(0x71),%1(0x1d),%1(0x29),%1(0xc5),%1(0x89) + db %1(0x6f),%1(0xb7),%1(0x62),%1(0x0e),%1(0xaa),%1(0x18),%1(0xbe),%1(0x1b) + db %1(0xfc),%1(0x56),%1(0x3e),%1(0x4b),%1(0xc6),%1(0xd2),%1(0x79),%1(0x20) + db %1(0x9a),%1(0xdb),%1(0xc0),%1(0xfe),%1(0x78),%1(0xcd),%1(0x5a),%1(0xf4) + db %1(0x1f),%1(0xdd),%1(0xa8),%1(0x33),%1(0x88),%1(0x07),%1(0xc7),%1(0x31) + db %1(0xb1),%1(0x12),%1(0x10),%1(0x59),%1(0x27),%1(0x80),%1(0xec),%1(0x5f) + db %1(0x60),%1(0x51),%1(0x7f),%1(0xa9),%1(0x19),%1(0xb5),%1(0x4a),%1(0x0d) + db %1(0x2d),%1(0xe5),%1(0x7a),%1(0x9f),%1(0x93),%1(0xc9),%1(0x9c),%1(0xef) + db %1(0xa0),%1(0xe0),%1(0x3b),%1(0x4d),%1(0xae),%1(0x2a),%1(0xf5),%1(0xb0) + db %1(0xc8),%1(0xeb),%1(0xbb),%1(0x3c),%1(0x83),%1(0x53),%1(0x99),%1(0x61) + db %1(0x17),%1(0x2b),%1(0x04),%1(0x7e),%1(0xba),%1(0x77),%1(0xd6),%1(0x26) + db %1(0xe1),%1(0x69),%1(0x14),%1(0x63),%1(0x55),%1(0x21),%1(0x0c),%1(0x7d) +%endmacro + +%define u8(x) f2(x), x, x, f3(x), f2(x), x, x, f3(x) +%define v8(x) fe(x), f9(x), fd(x), fb(x), fe(x), f9(x), fd(x), x +%define w8(x) x, 0, 0, 0, x, 0, 0, 0 + +%define tptr rbp ; table pointer +%define kptr r8 ; key schedule pointer +%define fofs 128 ; adjust offset in key schedule to keep |disp| < 128 +%define fk_ref(x,y) [kptr-16*x+fofs+4*y] +%ifdef AES_REV_DKS +%define rofs 128 +%define ik_ref(x,y) [kptr-16*x+rofs+4*y] +%else +%define rofs -128 +%define ik_ref(x,y) [kptr+16*x+rofs+4*y] +%endif + +%define tab_0(x) [tptr+8*x] +%define tab_1(x) [tptr+8*x+3] +%define tab_2(x) [tptr+8*x+2] +%define tab_3(x) [tptr+8*x+1] +%define tab_f(x) byte [tptr+8*x+1] +%define tab_i(x) byte [tptr+8*x+7] +%define t_ref(x,r) tab_ %+ x(r) + +%macro ff_rnd 5 ; normal forward round + mov %1d, fk_ref(%5,0) + mov %2d, fk_ref(%5,1) + mov %3d, fk_ref(%5,2) + mov %4d, fk_ref(%5,3) + + movzx esi, al + movzx edi, ah + shr eax, 16 + xor %1d, t_ref(0,rsi) + xor %4d, t_ref(1,rdi) + movzx esi, al + movzx edi, ah + xor %3d, t_ref(2,rsi) + xor %2d, t_ref(3,rdi) + + movzx esi, bl + movzx edi, bh + shr ebx, 16 + xor %2d, t_ref(0,rsi) + xor %1d, t_ref(1,rdi) + movzx esi, bl + movzx edi, bh + xor %4d, t_ref(2,rsi) + xor %3d, t_ref(3,rdi) + + movzx esi, cl + movzx edi, ch + shr ecx, 16 + xor %3d, t_ref(0,rsi) + xor %2d, t_ref(1,rdi) + movzx esi, cl + movzx edi, ch + xor %1d, t_ref(2,rsi) + xor %4d, t_ref(3,rdi) + + movzx esi, dl + movzx edi, dh + shr edx, 16 + xor %4d, t_ref(0,rsi) + xor %3d, t_ref(1,rdi) + movzx esi, dl + movzx edi, dh + xor %2d, t_ref(2,rsi) + xor %1d, t_ref(3,rdi) + + mov eax,%1d + mov ebx,%2d + mov ecx,%3d + mov edx,%4d +%endmacro + +%ifdef LAST_ROUND_TABLES + +%macro fl_rnd 5 ; last forward round + add tptr, 2048 + mov %1d, fk_ref(%5,0) + mov %2d, fk_ref(%5,1) + mov %3d, fk_ref(%5,2) + mov %4d, fk_ref(%5,3) + + movzx esi, al + movzx edi, ah + shr eax, 16 + xor %1d, t_ref(0,rsi) + xor %4d, t_ref(1,rdi) + movzx esi, al + movzx edi, ah + xor %3d, t_ref(2,rsi) + xor %2d, t_ref(3,rdi) + + movzx esi, bl + movzx edi, bh + shr ebx, 16 + xor %2d, t_ref(0,rsi) + xor %1d, t_ref(1,rdi) + movzx esi, bl + movzx edi, bh + xor %4d, t_ref(2,rsi) + xor %3d, t_ref(3,rdi) + + movzx esi, cl + movzx edi, ch + shr ecx, 16 + xor %3d, t_ref(0,rsi) + xor %2d, t_ref(1,rdi) + movzx esi, cl + movzx edi, ch + xor %1d, t_ref(2,rsi) + xor %4d, t_ref(3,rdi) + + movzx esi, dl + movzx edi, dh + shr edx, 16 + xor %4d, t_ref(0,rsi) + xor %3d, t_ref(1,rdi) + movzx esi, dl + movzx edi, dh + xor %2d, t_ref(2,rsi) + xor %1d, t_ref(3,rdi) +%endmacro + +%else + +%macro fl_rnd 5 ; last forward round + mov %1d, fk_ref(%5,0) + mov %2d, fk_ref(%5,1) + mov %3d, fk_ref(%5,2) + mov %4d, fk_ref(%5,3) + + movzx esi, al + movzx edi, ah + shr eax, 16 + movzx esi, t_ref(f,rsi) + movzx edi, t_ref(f,rdi) + xor %1d, esi + rol edi, 8 + xor %4d, edi + movzx esi, al + movzx edi, ah + movzx esi, t_ref(f,rsi) + movzx edi, t_ref(f,rdi) + rol esi, 16 + rol edi, 24 + xor %3d, esi + xor %2d, edi + + movzx esi, bl + movzx edi, bh + shr ebx, 16 + movzx esi, t_ref(f,rsi) + movzx edi, t_ref(f,rdi) + xor %2d, esi + rol edi, 8 + xor %1d, edi + movzx esi, bl + movzx edi, bh + movzx esi, t_ref(f,rsi) + movzx edi, t_ref(f,rdi) + rol esi, 16 + rol edi, 24 + xor %4d, esi + xor %3d, edi + + movzx esi, cl + movzx edi, ch + movzx esi, t_ref(f,rsi) + movzx edi, t_ref(f,rdi) + shr ecx, 16 + xor %3d, esi + rol edi, 8 + xor %2d, edi + movzx esi, cl + movzx edi, ch + movzx esi, t_ref(f,rsi) + movzx edi, t_ref(f,rdi) + rol esi, 16 + rol edi, 24 + xor %1d, esi + xor %4d, edi + + movzx esi, dl + movzx edi, dh + movzx esi, t_ref(f,rsi) + movzx edi, t_ref(f,rdi) + shr edx, 16 + xor %4d, esi + rol edi, 8 + xor %3d, edi + movzx esi, dl + movzx edi, dh + movzx esi, t_ref(f,rsi) + movzx edi, t_ref(f,rdi) + rol esi, 16 + rol edi, 24 + xor %2d, esi + xor %1d, edi +%endmacro + +%endif + +%macro ii_rnd 5 ; normal inverse round + mov %1d, ik_ref(%5,0) + mov %2d, ik_ref(%5,1) + mov %3d, ik_ref(%5,2) + mov %4d, ik_ref(%5,3) + + movzx esi, al + movzx edi, ah + shr eax, 16 + xor %1d, t_ref(0,rsi) + xor %2d, t_ref(1,rdi) + movzx esi, al + movzx edi, ah + xor %3d, t_ref(2,rsi) + xor %4d, t_ref(3,rdi) + + movzx esi, bl + movzx edi, bh + shr ebx, 16 + xor %2d, t_ref(0,rsi) + xor %3d, t_ref(1,rdi) + movzx esi, bl + movzx edi, bh + xor %4d, t_ref(2,rsi) + xor %1d, t_ref(3,rdi) + + movzx esi, cl + movzx edi, ch + shr ecx, 16 + xor %3d, t_ref(0,rsi) + xor %4d, t_ref(1,rdi) + movzx esi, cl + movzx edi, ch + xor %1d, t_ref(2,rsi) + xor %2d, t_ref(3,rdi) + + movzx esi, dl + movzx edi, dh + shr edx, 16 + xor %4d, t_ref(0,rsi) + xor %1d, t_ref(1,rdi) + movzx esi, dl + movzx edi, dh + xor %2d, t_ref(2,rsi) + xor %3d, t_ref(3,rdi) + + mov eax,%1d + mov ebx,%2d + mov ecx,%3d + mov edx,%4d +%endmacro + +%ifdef LAST_ROUND_TABLES + +%macro il_rnd 5 ; last inverse round + add tptr, 2048 + mov %1d, ik_ref(%5,0) + mov %2d, ik_ref(%5,1) + mov %3d, ik_ref(%5,2) + mov %4d, ik_ref(%5,3) + + movzx esi, al + movzx edi, ah + shr eax, 16 + xor %1d, t_ref(0,rsi) + xor %2d, t_ref(1,rdi) + movzx esi, al + movzx edi, ah + xor %3d, t_ref(2,rsi) + xor %4d, t_ref(3,rdi) + + movzx esi, bl + movzx edi, bh + shr ebx, 16 + xor %2d, t_ref(0,rsi) + xor %3d, t_ref(1,rdi) + movzx esi, bl + movzx edi, bh + xor %4d, t_ref(2,rsi) + xor %1d, t_ref(3,rdi) + + movzx esi, cl + movzx edi, ch + shr ecx, 16 + xor %3d, t_ref(0,rsi) + xor %4d, t_ref(1,rdi) + movzx esi, cl + movzx edi, ch + xor %1d, t_ref(2,rsi) + xor %2d, t_ref(3,rdi) + + movzx esi, dl + movzx edi, dh + shr edx, 16 + xor %4d, t_ref(0,rsi) + xor %1d, t_ref(1,rdi) + movzx esi, dl + movzx edi, dh + xor %2d, t_ref(2,rsi) + xor %3d, t_ref(3,rdi) +%endmacro + +%else + +%macro il_rnd 5 ; last inverse round + mov %1d, ik_ref(%5,0) + mov %2d, ik_ref(%5,1) + mov %3d, ik_ref(%5,2) + mov %4d, ik_ref(%5,3) + + movzx esi, al + movzx edi, ah + movzx esi, t_ref(i,rsi) + movzx edi, t_ref(i,rdi) + shr eax, 16 + xor %1d, esi + rol edi, 8 + xor %2d, edi + movzx esi, al + movzx edi, ah + movzx esi, t_ref(i,rsi) + movzx edi, t_ref(i,rdi) + rol esi, 16 + rol edi, 24 + xor %3d, esi + xor %4d, edi + + movzx esi, bl + movzx edi, bh + movzx esi, t_ref(i,rsi) + movzx edi, t_ref(i,rdi) + shr ebx, 16 + xor %2d, esi + rol edi, 8 + xor %3d, edi + movzx esi, bl + movzx edi, bh + movzx esi, t_ref(i,rsi) + movzx edi, t_ref(i,rdi) + rol esi, 16 + rol edi, 24 + xor %4d, esi + xor %1d, edi + + movzx esi, cl + movzx edi, ch + movzx esi, t_ref(i,rsi) + movzx edi, t_ref(i,rdi) + shr ecx, 16 + xor %3d, esi + rol edi, 8 + xor %4d, edi + movzx esi, cl + movzx edi, ch + movzx esi, t_ref(i,rsi) + movzx edi, t_ref(i,rdi) + rol esi, 16 + rol edi, 24 + xor %1d, esi + xor %2d, edi + + movzx esi, dl + movzx edi, dh + movzx esi, t_ref(i,rsi) + movzx edi, t_ref(i,rdi) + shr edx, 16 + xor %4d, esi + rol edi, 8 + xor %1d, edi + movzx esi, dl + movzx edi, dh + movzx esi, t_ref(i,rsi) + movzx edi, t_ref(i,rdi) + rol esi, 16 + rol edi, 24 + xor %2d, esi + xor %3d, edi +%endmacro + +%endif + +%ifdef ENCRYPTION + + global aes_encrypt +%ifdef DLL_EXPORT + export aes_encrypt +%endif + + section .data align=64 + align 64 +enc_tab: + enc_vals u8 +%ifdef LAST_ROUND_TABLES + enc_vals w8 +%endif + + section .text align=16 + align 16 + +%ifdef _SEH_ +proc_frame aes_encrypt + alloc_stack 7*8 ; 7 to align stack to 16 bytes + save_reg rsi,4*8 + save_reg rdi,5*8 + save_reg rbx,1*8 + save_reg rbp,2*8 + save_reg r12,3*8 +end_prologue + mov rdi, rcx ; input pointer + mov [rsp+0*8], rdx ; output pointer +%else + aes_encrypt: + %ifdef __GNUC__ + sub rsp, 4*8 ; gnu/linux binary interface + mov [rsp+0*8], rsi ; output pointer + mov r8, rdx ; context + %else + sub rsp, 6*8 ; windows binary interface + mov [rsp+4*8], rsi + mov [rsp+5*8], rdi + mov rdi, rcx ; input pointer + mov [rsp+0*8], rdx ; output pointer + %endif + mov [rsp+1*8], rbx ; input pointer in rdi + mov [rsp+2*8], rbp ; output pointer in [rsp] + mov [rsp+3*8], r12 ; context in r8 +%endif + + movzx esi, byte [kptr+4*KS_LENGTH] + lea tptr,[enc_tab wrt rip] + sub kptr, fofs + + mov eax, [rdi+0*4] + mov ebx, [rdi+1*4] + mov ecx, [rdi+2*4] + mov edx, [rdi+3*4] + + xor eax, [kptr+fofs] + xor ebx, [kptr+fofs+4] + xor ecx, [kptr+fofs+8] + xor edx, [kptr+fofs+12] + + lea kptr,[kptr+rsi] + cmp esi, 10*16 + je .3 + cmp esi, 12*16 + je .2 + cmp esi, 14*16 + je .1 + mov rax, -1 + jmp .4 + +.1: ff_rnd r9, r10, r11, r12, 13 + ff_rnd r9, r10, r11, r12, 12 +.2: ff_rnd r9, r10, r11, r12, 11 + ff_rnd r9, r10, r11, r12, 10 +.3: ff_rnd r9, r10, r11, r12, 9 + ff_rnd r9, r10, r11, r12, 8 + ff_rnd r9, r10, r11, r12, 7 + ff_rnd r9, r10, r11, r12, 6 + ff_rnd r9, r10, r11, r12, 5 + ff_rnd r9, r10, r11, r12, 4 + ff_rnd r9, r10, r11, r12, 3 + ff_rnd r9, r10, r11, r12, 2 + ff_rnd r9, r10, r11, r12, 1 + fl_rnd r9, r10, r11, r12, 0 + + mov rbx, [rsp] + mov [rbx], r9d + mov [rbx+4], r10d + mov [rbx+8], r11d + mov [rbx+12], r12d + xor rax, rax +.4: + mov rbx, [rsp+1*8] + mov rbp, [rsp+2*8] + mov r12, [rsp+3*8] +%ifdef __GNUC__ + add rsp, 4*8 + ret +%else + mov rsi, [rsp+4*8] + mov rdi, [rsp+5*8] + %ifdef _SEH_ + add rsp, 7*8 + ret + endproc_frame + %else + add rsp, 6*8 + ret + %endif +%endif + +%endif + +%ifdef DECRYPTION + + global aes_decrypt +%ifdef DLL_EXPORT + export aes_decrypt +%endif + + section .data + align 64 +dec_tab: + dec_vals v8 +%ifdef LAST_ROUND_TABLES + dec_vals w8 +%endif + + section .text + align 16 + +%ifdef _SEH_ +proc_frame aes_decrypt + alloc_stack 7*8 ; 7 to align stack to 16 bytes + save_reg rsi,4*8 + save_reg rdi,5*8 + save_reg rbx,1*8 + save_reg rbp,2*8 + save_reg r12,3*8 +end_prologue + mov rdi, rcx ; input pointer + mov [rsp+0*8], rdx ; output pointer +%else + aes_decrypt: + %ifdef __GNUC__ + sub rsp, 4*8 ; gnu/linux binary interface + mov [rsp+0*8], rsi ; output pointer + mov r8, rdx ; context + %else + sub rsp, 6*8 ; windows binary interface + mov [rsp+4*8], rsi + mov [rsp+5*8], rdi + mov rdi, rcx ; input pointer + mov [rsp+0*8], rdx ; output pointer + %endif + mov [rsp+1*8], rbx ; input pointer in rdi + mov [rsp+2*8], rbp ; output pointer in [rsp] + mov [rsp+3*8], r12 ; context in r8 +%endif + + movzx esi,byte[kptr+4*KS_LENGTH] + lea tptr,[dec_tab wrt rip] + sub kptr, rofs + + mov eax, [rdi+0*4] + mov ebx, [rdi+1*4] + mov ecx, [rdi+2*4] + mov edx, [rdi+3*4] + +%ifdef AES_REV_DKS + mov rdi, kptr + lea kptr,[kptr+rsi] +%else + lea rdi,[kptr+rsi] +%endif + + xor eax, [rdi+rofs] + xor ebx, [rdi+rofs+4] + xor ecx, [rdi+rofs+8] + xor edx, [rdi+rofs+12] + + cmp esi, 10*16 + je .3 + cmp esi, 12*16 + je .2 + cmp esi, 14*16 + je .1 + mov rax, -1 + jmp .4 + +.1: ii_rnd r9, r10, r11, r12, 13 + ii_rnd r9, r10, r11, r12, 12 +.2: ii_rnd r9, r10, r11, r12, 11 + ii_rnd r9, r10, r11, r12, 10 +.3: ii_rnd r9, r10, r11, r12, 9 + ii_rnd r9, r10, r11, r12, 8 + ii_rnd r9, r10, r11, r12, 7 + ii_rnd r9, r10, r11, r12, 6 + ii_rnd r9, r10, r11, r12, 5 + ii_rnd r9, r10, r11, r12, 4 + ii_rnd r9, r10, r11, r12, 3 + ii_rnd r9, r10, r11, r12, 2 + ii_rnd r9, r10, r11, r12, 1 + il_rnd r9, r10, r11, r12, 0 + + mov rbx, [rsp] + mov [rbx], r9d + mov [rbx+4], r10d + mov [rbx+8], r11d + mov [rbx+12], r12d + xor rax, rax +.4: mov rbx, [rsp+1*8] + mov rbp, [rsp+2*8] + mov r12, [rsp+3*8] +%ifdef __GNUC__ + add rsp, 4*8 + ret +%else + mov rsi, [rsp+4*8] + mov rdi, [rsp+5*8] + %ifdef _SEH_ + add rsp, 7*8 + ret + endproc_frame + %else + add rsp, 6*8 + ret + %endif +%endif + +%endif + + end diff --git a/src/java/KP2AKdbLibrary/jni/aes/aes_modes.c b/src/java/KP2AKdbLibrary/jni/aes/aes_modes.c new file mode 100644 index 00000000..01320b96 --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/aes_modes.c @@ -0,0 +1,945 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 20/12/2007 + + These subroutines implement multiple block AES modes for ECB, CBC, CFB, + OFB and CTR encryption, The code provides support for the VIA Advanced + Cryptography Engine (ACE). + + NOTE: In the following subroutines, the AES contexts (ctx) must be + 16 byte aligned if VIA ACE is being used +*/ + +#include +#include + +#include "aesopt.h" + +#if defined( AES_MODES ) +#if defined(__cplusplus) +extern "C" +{ +#endif + +#if defined( _MSC_VER ) && ( _MSC_VER > 800 ) +#pragma intrinsic(memcpy) +#endif + +#define BFR_BLOCKS 8 + +/* These values are used to detect long word alignment in order to */ +/* speed up some buffer operations. This facility may not work on */ +/* some machines so this define can be commented out if necessary */ + +#define FAST_BUFFER_OPERATIONS + +#define lp32(x) ((uint_32t*)(x)) + +#if defined( USE_VIA_ACE_IF_PRESENT ) + +#include "aes_via_ace.h" + +#pragma pack(16) + +aligned_array(unsigned long, enc_gen_table, 12, 16) = NEH_ENC_GEN_DATA; +aligned_array(unsigned long, enc_load_table, 12, 16) = NEH_ENC_LOAD_DATA; +aligned_array(unsigned long, enc_hybrid_table, 12, 16) = NEH_ENC_HYBRID_DATA; +aligned_array(unsigned long, dec_gen_table, 12, 16) = NEH_DEC_GEN_DATA; +aligned_array(unsigned long, dec_load_table, 12, 16) = NEH_DEC_LOAD_DATA; +aligned_array(unsigned long, dec_hybrid_table, 12, 16) = NEH_DEC_HYBRID_DATA; + +/* NOTE: These control word macros must only be used after */ +/* a key has been set up because they depend on key size */ + +#if NEH_KEY_TYPE == NEH_LOAD +#define kd_adr(c) ((uint_8t*)(c)->ks) +#elif NEH_KEY_TYPE == NEH_GENERATE +#define kd_adr(c) ((uint_8t*)(c)->ks + (c)->inf.b[0]) +#else +#define kd_adr(c) ((uint_8t*)(c)->ks + ((c)->inf.b[0] == 160 ? 160 : 0)) +#endif + +#else + +#define aligned_array(type, name, no, stride) type name[no] +#define aligned_auto(type, name, no, stride) type name[no] + +#endif + +#if defined( _MSC_VER ) && _MSC_VER > 1200 + +#define via_cwd(cwd, ty, dir, len) \ + unsigned long* cwd = (dir##_##ty##_table + ((len - 128) >> 4)) + +#else + +#define via_cwd(cwd, ty, dir, len) \ + aligned_auto(unsigned long, cwd, 4, 16); \ + cwd[1] = cwd[2] = cwd[3] = 0; \ + cwd[0] = neh_##dir##_##ty##_key(len) + +#endif + +/* test the code for detecting and setting pointer alignment */ + +AES_RETURN aes_test_alignment_detection(unsigned int n) /* 4 <= n <= 16 */ +{ uint_8t p[16]; + uint_32t i, count_eq = 0, count_neq = 0; + + if(n < 4 || n > 16) + return EXIT_FAILURE; + + for(i = 0; i < n; ++i) + { + uint_8t *qf = ALIGN_FLOOR(p + i, n), + *qh = ALIGN_CEIL(p + i, n); + + if(qh == qf) + ++count_eq; + else if(qh == qf + n) + ++count_neq; + else + return EXIT_FAILURE; + } + return (count_eq != 1 || count_neq != n - 1 ? EXIT_FAILURE : EXIT_SUCCESS); +} + +AES_RETURN aes_mode_reset(aes_encrypt_ctx ctx[1]) +{ + ctx->inf.b[2] = 0; + return EXIT_SUCCESS; +} + +AES_RETURN aes_ecb_encrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, const aes_encrypt_ctx ctx[1]) +{ int nb = len >> 4; + + if(len & (AES_BLOCK_SIZE - 1)) + return EXIT_FAILURE; + +#if defined( USE_VIA_ACE_IF_PRESENT ) + + if(ctx->inf.b[1] == 0xff) + { uint_8t *ksp = (uint_8t*)(ctx->ks); + via_cwd(cwd, hybrid, enc, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET( ctx, 16 )) + return EXIT_FAILURE; + + if(!ALIGN_OFFSET( ibuf, 16 ) && !ALIGN_OFFSET( obuf, 16 )) + { + via_ecb_op5(ksp, cwd, ibuf, obuf, nb); + } + else + { aligned_auto(uint_8t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint_8t *ip, *op; + + while(nb) + { + int m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb); + + ip = (ALIGN_OFFSET( ibuf, 16 ) ? buf : ibuf); + op = (ALIGN_OFFSET( obuf, 16 ) ? buf : obuf); + + if(ip != ibuf) + memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_ecb_op5(ksp, cwd, ip, op, m); + + if(op != obuf) + memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + nb -= m; + } + } + + return EXIT_SUCCESS; + } + +#endif + +#if !defined( ASSUME_VIA_ACE_PRESENT ) + while(nb--) + { + if(aes_encrypt(ibuf, obuf, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } +#endif + return EXIT_SUCCESS; +} + +AES_RETURN aes_ecb_decrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, const aes_decrypt_ctx ctx[1]) +{ int nb = len >> 4; + + if(len & (AES_BLOCK_SIZE - 1)) + return EXIT_FAILURE; + +#if defined( USE_VIA_ACE_IF_PRESENT ) + + if(ctx->inf.b[1] == 0xff) + { uint_8t *ksp = kd_adr(ctx); + via_cwd(cwd, hybrid, dec, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET( ctx, 16 )) + return EXIT_FAILURE; + + if(!ALIGN_OFFSET( ibuf, 16 ) && !ALIGN_OFFSET( obuf, 16 )) + { + via_ecb_op5(ksp, cwd, ibuf, obuf, nb); + } + else + { aligned_auto(uint_8t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint_8t *ip, *op; + + while(nb) + { + int m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb); + + ip = (ALIGN_OFFSET( ibuf, 16 ) ? buf : ibuf); + op = (ALIGN_OFFSET( obuf, 16 ) ? buf : obuf); + + if(ip != ibuf) + memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_ecb_op5(ksp, cwd, ip, op, m); + + if(op != obuf) + memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + nb -= m; + } + } + + return EXIT_SUCCESS; + } + +#endif + +#if !defined( ASSUME_VIA_ACE_PRESENT ) + while(nb--) + { + if(aes_decrypt(ibuf, obuf, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } +#endif + return EXIT_SUCCESS; +} + +AES_RETURN aes_cbc_encrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, const aes_encrypt_ctx ctx[1]) +{ int nb = len >> 4; + + if(len & (AES_BLOCK_SIZE - 1)) + return EXIT_FAILURE; + +#if defined( USE_VIA_ACE_IF_PRESENT ) + + if(ctx->inf.b[1] == 0xff) + { uint_8t *ksp = (uint_8t*)(ctx->ks), *ivp = iv; + aligned_auto(uint_8t, liv, AES_BLOCK_SIZE, 16); + via_cwd(cwd, hybrid, enc, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET( ctx, 16 )) + return EXIT_FAILURE; + + if(ALIGN_OFFSET( iv, 16 )) /* ensure an aligned iv */ + { + ivp = liv; + memcpy(liv, iv, AES_BLOCK_SIZE); + } + + if(!ALIGN_OFFSET( ibuf, 16 ) && !ALIGN_OFFSET( obuf, 16 ) && !ALIGN_OFFSET( iv, 16 )) + { + via_cbc_op7(ksp, cwd, ibuf, obuf, nb, ivp, ivp); + } + else + { aligned_auto(uint_8t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint_8t *ip, *op; + + while(nb) + { + int m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb); + + ip = (ALIGN_OFFSET( ibuf, 16 ) ? buf : ibuf); + op = (ALIGN_OFFSET( obuf, 16 ) ? buf : obuf); + + if(ip != ibuf) + memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_cbc_op7(ksp, cwd, ip, op, m, ivp, ivp); + + if(op != obuf) + memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + nb -= m; + } + } + + if(iv != ivp) + memcpy(iv, ivp, AES_BLOCK_SIZE); + + return EXIT_SUCCESS; + } + +#endif + +#if !defined( ASSUME_VIA_ACE_PRESENT ) +# ifdef FAST_BUFFER_OPERATIONS + if(!ALIGN_OFFSET( ibuf, 4 ) && !ALIGN_OFFSET( iv, 4 )) + while(nb--) + { + lp32(iv)[0] ^= lp32(ibuf)[0]; + lp32(iv)[1] ^= lp32(ibuf)[1]; + lp32(iv)[2] ^= lp32(ibuf)[2]; + lp32(iv)[3] ^= lp32(ibuf)[3]; + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + memcpy(obuf, iv, AES_BLOCK_SIZE); + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } + else +# endif + while(nb--) + { + iv[ 0] ^= ibuf[ 0]; iv[ 1] ^= ibuf[ 1]; + iv[ 2] ^= ibuf[ 2]; iv[ 3] ^= ibuf[ 3]; + iv[ 4] ^= ibuf[ 4]; iv[ 5] ^= ibuf[ 5]; + iv[ 6] ^= ibuf[ 6]; iv[ 7] ^= ibuf[ 7]; + iv[ 8] ^= ibuf[ 8]; iv[ 9] ^= ibuf[ 9]; + iv[10] ^= ibuf[10]; iv[11] ^= ibuf[11]; + iv[12] ^= ibuf[12]; iv[13] ^= ibuf[13]; + iv[14] ^= ibuf[14]; iv[15] ^= ibuf[15]; + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + memcpy(obuf, iv, AES_BLOCK_SIZE); + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } +#endif + return EXIT_SUCCESS; +} + +AES_RETURN aes_cbc_decrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, const aes_decrypt_ctx ctx[1]) +{ unsigned char tmp[AES_BLOCK_SIZE]; + int nb = len >> 4; + + if(len & (AES_BLOCK_SIZE - 1)) + return EXIT_FAILURE; + +#if defined( USE_VIA_ACE_IF_PRESENT ) + + if(ctx->inf.b[1] == 0xff) + { uint_8t *ksp = kd_adr(ctx), *ivp = iv; + aligned_auto(uint_8t, liv, AES_BLOCK_SIZE, 16); + via_cwd(cwd, hybrid, dec, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET( ctx, 16 )) + return EXIT_FAILURE; + + if(ALIGN_OFFSET( iv, 16 )) /* ensure an aligned iv */ + { + ivp = liv; + memcpy(liv, iv, AES_BLOCK_SIZE); + } + + if(!ALIGN_OFFSET( ibuf, 16 ) && !ALIGN_OFFSET( obuf, 16 ) && !ALIGN_OFFSET( iv, 16 )) + { + via_cbc_op6(ksp, cwd, ibuf, obuf, nb, ivp); + } + else + { aligned_auto(uint_8t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint_8t *ip, *op; + + while(nb) + { + int m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb); + + ip = (ALIGN_OFFSET( ibuf, 16 ) ? buf : ibuf); + op = (ALIGN_OFFSET( obuf, 16 ) ? buf : obuf); + + if(ip != ibuf) + memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_cbc_op6(ksp, cwd, ip, op, m, ivp); + + if(op != obuf) + memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + nb -= m; + } + } + + if(iv != ivp) + memcpy(iv, ivp, AES_BLOCK_SIZE); + + return EXIT_SUCCESS; + } +#endif + +#if !defined( ASSUME_VIA_ACE_PRESENT ) +# ifdef FAST_BUFFER_OPERATIONS + if(!ALIGN_OFFSET( obuf, 4 ) && !ALIGN_OFFSET( iv, 4 )) + while(nb--) + { + memcpy(tmp, ibuf, AES_BLOCK_SIZE); + if(aes_decrypt(ibuf, obuf, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + lp32(obuf)[0] ^= lp32(iv)[0]; + lp32(obuf)[1] ^= lp32(iv)[1]; + lp32(obuf)[2] ^= lp32(iv)[2]; + lp32(obuf)[3] ^= lp32(iv)[3]; + memcpy(iv, tmp, AES_BLOCK_SIZE); + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } + else +# endif + while(nb--) + { + memcpy(tmp, ibuf, AES_BLOCK_SIZE); + if(aes_decrypt(ibuf, obuf, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + obuf[ 0] ^= iv[ 0]; obuf[ 1] ^= iv[ 1]; + obuf[ 2] ^= iv[ 2]; obuf[ 3] ^= iv[ 3]; + obuf[ 4] ^= iv[ 4]; obuf[ 5] ^= iv[ 5]; + obuf[ 6] ^= iv[ 6]; obuf[ 7] ^= iv[ 7]; + obuf[ 8] ^= iv[ 8]; obuf[ 9] ^= iv[ 9]; + obuf[10] ^= iv[10]; obuf[11] ^= iv[11]; + obuf[12] ^= iv[12]; obuf[13] ^= iv[13]; + obuf[14] ^= iv[14]; obuf[15] ^= iv[15]; + memcpy(iv, tmp, AES_BLOCK_SIZE); + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } +#endif + return EXIT_SUCCESS; +} + +AES_RETURN aes_cfb_encrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, aes_encrypt_ctx ctx[1]) +{ int cnt = 0, b_pos = (int)ctx->inf.b[2], nb; + + if(b_pos) /* complete any partial block */ + { + while(b_pos < AES_BLOCK_SIZE && cnt < len) + { + *obuf++ = (iv[b_pos++] ^= *ibuf++); + cnt++; + } + + b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos); + } + + if((nb = (len - cnt) >> 4) != 0) /* process whole blocks */ + { +#if defined( USE_VIA_ACE_IF_PRESENT ) + + if(ctx->inf.b[1] == 0xff) + { int m; + uint_8t *ksp = (uint_8t*)(ctx->ks), *ivp = iv; + aligned_auto(uint_8t, liv, AES_BLOCK_SIZE, 16); + via_cwd(cwd, hybrid, enc, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET( ctx, 16 )) + return EXIT_FAILURE; + + if(ALIGN_OFFSET( iv, 16 )) /* ensure an aligned iv */ + { + ivp = liv; + memcpy(liv, iv, AES_BLOCK_SIZE); + } + + if(!ALIGN_OFFSET( ibuf, 16 ) && !ALIGN_OFFSET( obuf, 16 )) + { + via_cfb_op7(ksp, cwd, ibuf, obuf, nb, ivp, ivp); + ibuf += nb * AES_BLOCK_SIZE; + obuf += nb * AES_BLOCK_SIZE; + cnt += nb * AES_BLOCK_SIZE; + } + else /* input, output or both are unaligned */ + { aligned_auto(uint_8t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint_8t *ip, *op; + + while(nb) + { + m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb), nb -= m; + + ip = (ALIGN_OFFSET( ibuf, 16 ) ? buf : ibuf); + op = (ALIGN_OFFSET( obuf, 16 ) ? buf : obuf); + + if(ip != ibuf) + memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_cfb_op7(ksp, cwd, ip, op, m, ivp, ivp); + + if(op != obuf) + memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + cnt += m * AES_BLOCK_SIZE; + } + } + + if(ivp != iv) + memcpy(iv, ivp, AES_BLOCK_SIZE); + } +#else +# ifdef FAST_BUFFER_OPERATIONS + if(!ALIGN_OFFSET( ibuf, 4 ) && !ALIGN_OFFSET( obuf, 4 ) && !ALIGN_OFFSET( iv, 4 )) + while(cnt + AES_BLOCK_SIZE <= len) + { + assert(b_pos == 0); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + lp32(obuf)[0] = lp32(iv)[0] ^= lp32(ibuf)[0]; + lp32(obuf)[1] = lp32(iv)[1] ^= lp32(ibuf)[1]; + lp32(obuf)[2] = lp32(iv)[2] ^= lp32(ibuf)[2]; + lp32(obuf)[3] = lp32(iv)[3] ^= lp32(ibuf)[3]; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + cnt += AES_BLOCK_SIZE; + } + else +# endif + while(cnt + AES_BLOCK_SIZE <= len) + { + assert(b_pos == 0); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + obuf[ 0] = iv[ 0] ^= ibuf[ 0]; obuf[ 1] = iv[ 1] ^= ibuf[ 1]; + obuf[ 2] = iv[ 2] ^= ibuf[ 2]; obuf[ 3] = iv[ 3] ^= ibuf[ 3]; + obuf[ 4] = iv[ 4] ^= ibuf[ 4]; obuf[ 5] = iv[ 5] ^= ibuf[ 5]; + obuf[ 6] = iv[ 6] ^= ibuf[ 6]; obuf[ 7] = iv[ 7] ^= ibuf[ 7]; + obuf[ 8] = iv[ 8] ^= ibuf[ 8]; obuf[ 9] = iv[ 9] ^= ibuf[ 9]; + obuf[10] = iv[10] ^= ibuf[10]; obuf[11] = iv[11] ^= ibuf[11]; + obuf[12] = iv[12] ^= ibuf[12]; obuf[13] = iv[13] ^= ibuf[13]; + obuf[14] = iv[14] ^= ibuf[14]; obuf[15] = iv[15] ^= ibuf[15]; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + cnt += AES_BLOCK_SIZE; + } +#endif + } + + while(cnt < len) + { + if(!b_pos && aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + + while(cnt < len && b_pos < AES_BLOCK_SIZE) + { + *obuf++ = (iv[b_pos++] ^= *ibuf++); + cnt++; + } + + b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos); + } + + ctx->inf.b[2] = (uint_8t)b_pos; + return EXIT_SUCCESS; +} + +AES_RETURN aes_cfb_decrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, aes_encrypt_ctx ctx[1]) +{ int cnt = 0, b_pos = (int)ctx->inf.b[2], nb; + + if(b_pos) /* complete any partial block */ + { uint_8t t; + + while(b_pos < AES_BLOCK_SIZE && cnt < len) + { + t = *ibuf++; + *obuf++ = t ^ iv[b_pos]; + iv[b_pos++] = t; + cnt++; + } + + b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos); + } + + if((nb = (len - cnt) >> 4) != 0) /* process whole blocks */ + { +#if defined( USE_VIA_ACE_IF_PRESENT ) + + if(ctx->inf.b[1] == 0xff) + { int m; + uint_8t *ksp = (uint_8t*)(ctx->ks), *ivp = iv; + aligned_auto(uint_8t, liv, AES_BLOCK_SIZE, 16); + via_cwd(cwd, hybrid, dec, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET( ctx, 16 )) + return EXIT_FAILURE; + + if(ALIGN_OFFSET( iv, 16 )) /* ensure an aligned iv */ + { + ivp = liv; + memcpy(liv, iv, AES_BLOCK_SIZE); + } + + if(!ALIGN_OFFSET( ibuf, 16 ) && !ALIGN_OFFSET( obuf, 16 )) + { + via_cfb_op6(ksp, cwd, ibuf, obuf, nb, ivp); + ibuf += nb * AES_BLOCK_SIZE; + obuf += nb * AES_BLOCK_SIZE; + cnt += nb * AES_BLOCK_SIZE; + } + else /* input, output or both are unaligned */ + { aligned_auto(uint_8t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint_8t *ip, *op; + + while(nb) + { + m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb), nb -= m; + + ip = (ALIGN_OFFSET( ibuf, 16 ) ? buf : ibuf); + op = (ALIGN_OFFSET( obuf, 16 ) ? buf : obuf); + + if(ip != ibuf) /* input buffer is not aligned */ + memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_cfb_op6(ksp, cwd, ip, op, m, ivp); + + if(op != obuf) /* output buffer is not aligned */ + memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + cnt += m * AES_BLOCK_SIZE; + } + } + + if(ivp != iv) + memcpy(iv, ivp, AES_BLOCK_SIZE); + } +#else +# ifdef FAST_BUFFER_OPERATIONS + if(!ALIGN_OFFSET( ibuf, 4 ) && !ALIGN_OFFSET( obuf, 4 ) &&!ALIGN_OFFSET( iv, 4 )) + while(cnt + AES_BLOCK_SIZE <= len) + { uint_32t t; + + assert(b_pos == 0); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + t = lp32(ibuf)[0], lp32(obuf)[0] = t ^ lp32(iv)[0], lp32(iv)[0] = t; + t = lp32(ibuf)[1], lp32(obuf)[1] = t ^ lp32(iv)[1], lp32(iv)[1] = t; + t = lp32(ibuf)[2], lp32(obuf)[2] = t ^ lp32(iv)[2], lp32(iv)[2] = t; + t = lp32(ibuf)[3], lp32(obuf)[3] = t ^ lp32(iv)[3], lp32(iv)[3] = t; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + cnt += AES_BLOCK_SIZE; + } + else +# endif + while(cnt + AES_BLOCK_SIZE <= len) + { uint_8t t; + + assert(b_pos == 0); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + t = ibuf[ 0], obuf[ 0] = t ^ iv[ 0], iv[ 0] = t; + t = ibuf[ 1], obuf[ 1] = t ^ iv[ 1], iv[ 1] = t; + t = ibuf[ 2], obuf[ 2] = t ^ iv[ 2], iv[ 2] = t; + t = ibuf[ 3], obuf[ 3] = t ^ iv[ 3], iv[ 3] = t; + t = ibuf[ 4], obuf[ 4] = t ^ iv[ 4], iv[ 4] = t; + t = ibuf[ 5], obuf[ 5] = t ^ iv[ 5], iv[ 5] = t; + t = ibuf[ 6], obuf[ 6] = t ^ iv[ 6], iv[ 6] = t; + t = ibuf[ 7], obuf[ 7] = t ^ iv[ 7], iv[ 7] = t; + t = ibuf[ 8], obuf[ 8] = t ^ iv[ 8], iv[ 8] = t; + t = ibuf[ 9], obuf[ 9] = t ^ iv[ 9], iv[ 9] = t; + t = ibuf[10], obuf[10] = t ^ iv[10], iv[10] = t; + t = ibuf[11], obuf[11] = t ^ iv[11], iv[11] = t; + t = ibuf[12], obuf[12] = t ^ iv[12], iv[12] = t; + t = ibuf[13], obuf[13] = t ^ iv[13], iv[13] = t; + t = ibuf[14], obuf[14] = t ^ iv[14], iv[14] = t; + t = ibuf[15], obuf[15] = t ^ iv[15], iv[15] = t; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + cnt += AES_BLOCK_SIZE; + } +#endif + } + + while(cnt < len) + { uint_8t t; + + if(!b_pos && aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + + while(cnt < len && b_pos < AES_BLOCK_SIZE) + { + t = *ibuf++; + *obuf++ = t ^ iv[b_pos]; + iv[b_pos++] = t; + cnt++; + } + + b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos); + } + + ctx->inf.b[2] = (uint_8t)b_pos; + return EXIT_SUCCESS; +} + +AES_RETURN aes_ofb_crypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, aes_encrypt_ctx ctx[1]) +{ int cnt = 0, b_pos = (int)ctx->inf.b[2], nb; + + if(b_pos) /* complete any partial block */ + { + while(b_pos < AES_BLOCK_SIZE && cnt < len) + { + *obuf++ = iv[b_pos++] ^ *ibuf++; + cnt++; + } + + b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos); + } + + if((nb = (len - cnt) >> 4) != 0) /* process whole blocks */ + { +#if defined( USE_VIA_ACE_IF_PRESENT ) + + if(ctx->inf.b[1] == 0xff) + { int m; + uint_8t *ksp = (uint_8t*)(ctx->ks), *ivp = iv; + aligned_auto(uint_8t, liv, AES_BLOCK_SIZE, 16); + via_cwd(cwd, hybrid, enc, 2 * ctx->inf.b[0] - 192); + + if(ALIGN_OFFSET( ctx, 16 )) + return EXIT_FAILURE; + + if(ALIGN_OFFSET( iv, 16 )) /* ensure an aligned iv */ + { + ivp = liv; + memcpy(liv, iv, AES_BLOCK_SIZE); + } + + if(!ALIGN_OFFSET( ibuf, 16 ) && !ALIGN_OFFSET( obuf, 16 )) + { + via_ofb_op6(ksp, cwd, ibuf, obuf, nb, ivp); + ibuf += nb * AES_BLOCK_SIZE; + obuf += nb * AES_BLOCK_SIZE; + cnt += nb * AES_BLOCK_SIZE; + } + else /* input, output or both are unaligned */ + { aligned_auto(uint_8t, buf, BFR_BLOCKS * AES_BLOCK_SIZE, 16); + uint_8t *ip, *op; + + while(nb) + { + m = (nb > BFR_BLOCKS ? BFR_BLOCKS : nb), nb -= m; + + ip = (ALIGN_OFFSET( ibuf, 16 ) ? buf : ibuf); + op = (ALIGN_OFFSET( obuf, 16 ) ? buf : obuf); + + if(ip != ibuf) + memcpy(buf, ibuf, m * AES_BLOCK_SIZE); + + via_ofb_op6(ksp, cwd, ip, op, m, ivp); + + if(op != obuf) + memcpy(obuf, buf, m * AES_BLOCK_SIZE); + + ibuf += m * AES_BLOCK_SIZE; + obuf += m * AES_BLOCK_SIZE; + cnt += m * AES_BLOCK_SIZE; + } + } + + if(ivp != iv) + memcpy(iv, ivp, AES_BLOCK_SIZE); + } +#else +# ifdef FAST_BUFFER_OPERATIONS + if(!ALIGN_OFFSET( ibuf, 4 ) && !ALIGN_OFFSET( obuf, 4 ) && !ALIGN_OFFSET( iv, 4 )) + while(cnt + AES_BLOCK_SIZE <= len) + { + assert(b_pos == 0); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + lp32(obuf)[0] = lp32(iv)[0] ^ lp32(ibuf)[0]; + lp32(obuf)[1] = lp32(iv)[1] ^ lp32(ibuf)[1]; + lp32(obuf)[2] = lp32(iv)[2] ^ lp32(ibuf)[2]; + lp32(obuf)[3] = lp32(iv)[3] ^ lp32(ibuf)[3]; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + cnt += AES_BLOCK_SIZE; + } + else +# endif + while(cnt + AES_BLOCK_SIZE <= len) + { + assert(b_pos == 0); + if(aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + obuf[ 0] = iv[ 0] ^ ibuf[ 0]; obuf[ 1] = iv[ 1] ^ ibuf[ 1]; + obuf[ 2] = iv[ 2] ^ ibuf[ 2]; obuf[ 3] = iv[ 3] ^ ibuf[ 3]; + obuf[ 4] = iv[ 4] ^ ibuf[ 4]; obuf[ 5] = iv[ 5] ^ ibuf[ 5]; + obuf[ 6] = iv[ 6] ^ ibuf[ 6]; obuf[ 7] = iv[ 7] ^ ibuf[ 7]; + obuf[ 8] = iv[ 8] ^ ibuf[ 8]; obuf[ 9] = iv[ 9] ^ ibuf[ 9]; + obuf[10] = iv[10] ^ ibuf[10]; obuf[11] = iv[11] ^ ibuf[11]; + obuf[12] = iv[12] ^ ibuf[12]; obuf[13] = iv[13] ^ ibuf[13]; + obuf[14] = iv[14] ^ ibuf[14]; obuf[15] = iv[15] ^ ibuf[15]; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + cnt += AES_BLOCK_SIZE; + } +#endif + } + + while(cnt < len) + { + if(!b_pos && aes_encrypt(iv, iv, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + + while(cnt < len && b_pos < AES_BLOCK_SIZE) + { + *obuf++ = iv[b_pos++] ^ *ibuf++; + cnt++; + } + + b_pos = (b_pos == AES_BLOCK_SIZE ? 0 : b_pos); + } + + ctx->inf.b[2] = (uint_8t)b_pos; + return EXIT_SUCCESS; +} + +#define BFR_LENGTH (BFR_BLOCKS * AES_BLOCK_SIZE) + +AES_RETURN aes_ctr_crypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *cbuf, cbuf_inc ctr_inc, aes_encrypt_ctx ctx[1]) +{ unsigned char *ip; + int i, blen, b_pos = (int)(ctx->inf.b[2]); + +#if defined( USE_VIA_ACE_IF_PRESENT ) + aligned_auto(uint_8t, buf, BFR_LENGTH, 16); + if(ctx->inf.b[1] == 0xff && ALIGN_OFFSET( ctx, 16 )) + return EXIT_FAILURE; +#else + uint_8t buf[BFR_LENGTH]; +#endif + + if(b_pos) + { + memcpy(buf, cbuf, AES_BLOCK_SIZE); + if(aes_ecb_encrypt(buf, buf, AES_BLOCK_SIZE, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + + while(b_pos < AES_BLOCK_SIZE && len) + { + *obuf++ = *ibuf++ ^ buf[b_pos++]; + --len; + } + + if(len) + ctr_inc(cbuf), b_pos = 0; + } + + while(len) + { + blen = (len > BFR_LENGTH ? BFR_LENGTH : len), len -= blen; + + for(i = 0, ip = buf; i < (blen >> 4); ++i) + { + memcpy(ip, cbuf, AES_BLOCK_SIZE); + ctr_inc(cbuf); + ip += AES_BLOCK_SIZE; + } + + if(blen & (AES_BLOCK_SIZE - 1)) + memcpy(ip, cbuf, AES_BLOCK_SIZE), i++; + +#if defined( USE_VIA_ACE_IF_PRESENT ) + if(ctx->inf.b[1] == 0xff) + { + via_cwd(cwd, hybrid, enc, 2 * ctx->inf.b[0] - 192); + via_ecb_op5((ctx->ks), cwd, buf, buf, i); + } + else +#endif + if(aes_ecb_encrypt(buf, buf, i * AES_BLOCK_SIZE, ctx) != EXIT_SUCCESS) + return EXIT_FAILURE; + + i = 0; ip = buf; +# ifdef FAST_BUFFER_OPERATIONS + if(!ALIGN_OFFSET( ibuf, 4 ) && !ALIGN_OFFSET( obuf, 4 ) && !ALIGN_OFFSET( ip, 4 )) + while(i + AES_BLOCK_SIZE <= blen) + { + lp32(obuf)[0] = lp32(ibuf)[0] ^ lp32(ip)[0]; + lp32(obuf)[1] = lp32(ibuf)[1] ^ lp32(ip)[1]; + lp32(obuf)[2] = lp32(ibuf)[2] ^ lp32(ip)[2]; + lp32(obuf)[3] = lp32(ibuf)[3] ^ lp32(ip)[3]; + i += AES_BLOCK_SIZE; + ip += AES_BLOCK_SIZE; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } + else +#endif + while(i + AES_BLOCK_SIZE <= blen) + { + obuf[ 0] = ibuf[ 0] ^ ip[ 0]; obuf[ 1] = ibuf[ 1] ^ ip[ 1]; + obuf[ 2] = ibuf[ 2] ^ ip[ 2]; obuf[ 3] = ibuf[ 3] ^ ip[ 3]; + obuf[ 4] = ibuf[ 4] ^ ip[ 4]; obuf[ 5] = ibuf[ 5] ^ ip[ 5]; + obuf[ 6] = ibuf[ 6] ^ ip[ 6]; obuf[ 7] = ibuf[ 7] ^ ip[ 7]; + obuf[ 8] = ibuf[ 8] ^ ip[ 8]; obuf[ 9] = ibuf[ 9] ^ ip[ 9]; + obuf[10] = ibuf[10] ^ ip[10]; obuf[11] = ibuf[11] ^ ip[11]; + obuf[12] = ibuf[12] ^ ip[12]; obuf[13] = ibuf[13] ^ ip[13]; + obuf[14] = ibuf[14] ^ ip[14]; obuf[15] = ibuf[15] ^ ip[15]; + i += AES_BLOCK_SIZE; + ip += AES_BLOCK_SIZE; + ibuf += AES_BLOCK_SIZE; + obuf += AES_BLOCK_SIZE; + } + + while(i++ < blen) + *obuf++ = *ibuf++ ^ ip[b_pos++]; + } + + ctx->inf.b[2] = (uint_8t)b_pos; + return EXIT_SUCCESS; +} + +#if defined(__cplusplus) +} +#endif +#endif diff --git a/src/java/KP2AKdbLibrary/jni/aes/aes_via_ace.h b/src/java/KP2AKdbLibrary/jni/aes/aes_via_ace.h new file mode 100644 index 00000000..e6cc76af --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/aes_via_ace.h @@ -0,0 +1,529 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 20/12/20077 +*/ + +#ifndef AES_VIA_ACE_H +#define AES_VIA_ACE_H + +#if defined( _MSC_VER ) +# define INLINE __inline +#elif defined( __GNUC__ ) +# define INLINE static inline +#else +# error VIA ACE requires Microsoft or GNU C +#endif + +#define NEH_GENERATE 1 +#define NEH_LOAD 2 +#define NEH_HYBRID 3 + +#define MAX_READ_ATTEMPTS 1000 + +/* VIA Nehemiah RNG and ACE Feature Mask Values */ + +#define NEH_CPU_IS_VIA 0x00000001 +#define NEH_CPU_READ 0x00000010 +#define NEH_CPU_MASK 0x00000011 + +#define NEH_RNG_PRESENT 0x00000004 +#define NEH_RNG_ENABLED 0x00000008 +#define NEH_ACE_PRESENT 0x00000040 +#define NEH_ACE_ENABLED 0x00000080 +#define NEH_RNG_FLAGS (NEH_RNG_PRESENT | NEH_RNG_ENABLED) +#define NEH_ACE_FLAGS (NEH_ACE_PRESENT | NEH_ACE_ENABLED) +#define NEH_FLAGS_MASK (NEH_RNG_FLAGS | NEH_ACE_FLAGS) + +/* VIA Nehemiah Advanced Cryptography Engine (ACE) Control Word Values */ + +#define NEH_GEN_KEY 0x00000000 /* generate key schedule */ +#define NEH_LOAD_KEY 0x00000080 /* load schedule from memory */ +#define NEH_ENCRYPT 0x00000000 /* encryption */ +#define NEH_DECRYPT 0x00000200 /* decryption */ +#define NEH_KEY128 0x00000000+0x0a /* 128 bit key */ +#define NEH_KEY192 0x00000400+0x0c /* 192 bit key */ +#define NEH_KEY256 0x00000800+0x0e /* 256 bit key */ + +#define NEH_ENC_GEN (NEH_ENCRYPT | NEH_GEN_KEY) +#define NEH_DEC_GEN (NEH_DECRYPT | NEH_GEN_KEY) +#define NEH_ENC_LOAD (NEH_ENCRYPT | NEH_LOAD_KEY) +#define NEH_DEC_LOAD (NEH_DECRYPT | NEH_LOAD_KEY) + +#define NEH_ENC_GEN_DATA {\ + NEH_ENC_GEN | NEH_KEY128, 0, 0, 0,\ + NEH_ENC_GEN | NEH_KEY192, 0, 0, 0,\ + NEH_ENC_GEN | NEH_KEY256, 0, 0, 0 } + +#define NEH_ENC_LOAD_DATA {\ + NEH_ENC_LOAD | NEH_KEY128, 0, 0, 0,\ + NEH_ENC_LOAD | NEH_KEY192, 0, 0, 0,\ + NEH_ENC_LOAD | NEH_KEY256, 0, 0, 0 } + +#define NEH_ENC_HYBRID_DATA {\ + NEH_ENC_GEN | NEH_KEY128, 0, 0, 0,\ + NEH_ENC_LOAD | NEH_KEY192, 0, 0, 0,\ + NEH_ENC_LOAD | NEH_KEY256, 0, 0, 0 } + +#define NEH_DEC_GEN_DATA {\ + NEH_DEC_GEN | NEH_KEY128, 0, 0, 0,\ + NEH_DEC_GEN | NEH_KEY192, 0, 0, 0,\ + NEH_DEC_GEN | NEH_KEY256, 0, 0, 0 } + +#define NEH_DEC_LOAD_DATA {\ + NEH_DEC_LOAD | NEH_KEY128, 0, 0, 0,\ + NEH_DEC_LOAD | NEH_KEY192, 0, 0, 0,\ + NEH_DEC_LOAD | NEH_KEY256, 0, 0, 0 } + +#define NEH_DEC_HYBRID_DATA {\ + NEH_DEC_GEN | NEH_KEY128, 0, 0, 0,\ + NEH_DEC_LOAD | NEH_KEY192, 0, 0, 0,\ + NEH_DEC_LOAD | NEH_KEY256, 0, 0, 0 } + +#define neh_enc_gen_key(x) ((x) == 128 ? (NEH_ENC_GEN | NEH_KEY128) : \ + (x) == 192 ? (NEH_ENC_GEN | NEH_KEY192) : (NEH_ENC_GEN | NEH_KEY256)) + +#define neh_enc_load_key(x) ((x) == 128 ? (NEH_ENC_LOAD | NEH_KEY128) : \ + (x) == 192 ? (NEH_ENC_LOAD | NEH_KEY192) : (NEH_ENC_LOAD | NEH_KEY256)) + +#define neh_enc_hybrid_key(x) ((x) == 128 ? (NEH_ENC_GEN | NEH_KEY128) : \ + (x) == 192 ? (NEH_ENC_LOAD | NEH_KEY192) : (NEH_ENC_LOAD | NEH_KEY256)) + +#define neh_dec_gen_key(x) ((x) == 128 ? (NEH_DEC_GEN | NEH_KEY128) : \ + (x) == 192 ? (NEH_DEC_GEN | NEH_KEY192) : (NEH_DEC_GEN | NEH_KEY256)) + +#define neh_dec_load_key(x) ((x) == 128 ? (NEH_DEC_LOAD | NEH_KEY128) : \ + (x) == 192 ? (NEH_DEC_LOAD | NEH_KEY192) : (NEH_DEC_LOAD | NEH_KEY256)) + +#define neh_dec_hybrid_key(x) ((x) == 128 ? (NEH_DEC_GEN | NEH_KEY128) : \ + (x) == 192 ? (NEH_DEC_LOAD | NEH_KEY192) : (NEH_DEC_LOAD | NEH_KEY256)) + +#if defined( _MSC_VER ) && ( _MSC_VER > 1200 ) +#define aligned_auto(type, name, no, stride) __declspec(align(stride)) type name[no] +#else +#define aligned_auto(type, name, no, stride) \ + unsigned char _##name[no * sizeof(type) + stride]; \ + type *name = (type*)(16 * ((((unsigned long)(_##name)) + stride - 1) / stride)) +#endif + +#if defined( _MSC_VER ) && ( _MSC_VER > 1200 ) +#define aligned_array(type, name, no, stride) __declspec(align(stride)) type name[no] +#elif defined( __GNUC__ ) +#define aligned_array(type, name, no, stride) type name[no] __attribute__ ((aligned(stride))) +#else +#define aligned_array(type, name, no, stride) type name[no] +#endif + +/* VIA ACE codeword */ + +static unsigned char via_flags = 0; + +#if defined ( _MSC_VER ) && ( _MSC_VER > 800 ) + +#define NEH_REKEY __asm pushfd __asm popfd +#define NEH_AES __asm _emit 0xf3 __asm _emit 0x0f __asm _emit 0xa7 +#define NEH_ECB NEH_AES __asm _emit 0xc8 +#define NEH_CBC NEH_AES __asm _emit 0xd0 +#define NEH_CFB NEH_AES __asm _emit 0xe0 +#define NEH_OFB NEH_AES __asm _emit 0xe8 +#define NEH_RNG __asm _emit 0x0f __asm _emit 0xa7 __asm _emit 0xc0 + +INLINE int has_cpuid(void) +{ char ret_value; + __asm + { pushfd /* save EFLAGS register */ + mov eax,[esp] /* copy it to eax */ + mov edx,0x00200000 /* CPUID bit position */ + xor eax,edx /* toggle the CPUID bit */ + push eax /* attempt to set EFLAGS to */ + popfd /* the new value */ + pushfd /* get the new EFLAGS value */ + pop eax /* into eax */ + xor eax,[esp] /* xor with original value */ + and eax,edx /* has CPUID bit changed? */ + setne al /* set to 1 if we have been */ + mov ret_value,al /* able to change it */ + popfd /* restore original EFLAGS */ + } + return (int)ret_value; +} + +INLINE int is_via_cpu(void) +{ char ret_value; + __asm + { xor eax,eax /* use CPUID to get vendor */ + cpuid /* identity string */ + xor eax,eax /* is it "CentaurHauls" ? */ + sub ebx,0x746e6543 /* 'Cent' */ + or eax,ebx + sub edx,0x48727561 /* 'aurH' */ + or eax,edx + sub ecx,0x736c7561 /* 'auls' */ + or eax,ecx + sete al /* set to 1 if it is VIA ID */ + mov dl,NEH_CPU_READ /* mark CPU type as read */ + or dl,al /* & store result in flags */ + mov [via_flags],dl /* set VIA detected flag */ + mov ret_value,al /* able to change it */ + } + return (int)ret_value; +} + +INLINE int read_via_flags(void) +{ char ret_value = 0; + __asm + { + mov eax,0xC0000000 /* Centaur extended CPUID */ + cpuid + mov edx,0xc0000001 /* >= 0xc0000001 if support */ + cmp eax,edx /* for VIA extended feature */ + jnae no_rng /* flags is available */ + mov eax,edx /* read Centaur extended */ + cpuid /* feature flags */ + mov eax,NEH_FLAGS_MASK /* mask out and save */ + and eax,edx /* the RNG and ACE flags */ + or [via_flags],al /* present & enabled flags */ + mov ret_value,al /* able to change it */ +no_rng: + } + return (int)ret_value; +} + +INLINE unsigned int via_rng_in(void *buf) +{ char ret_value = 0x1f; + __asm + { + push edi + mov edi,buf /* input buffer address */ + xor edx,edx /* try to fetch 8 bytes */ + NEH_RNG /* do RNG read operation */ + and ret_value,al /* count of bytes returned */ + pop edi + } + return (int)ret_value; +} + +INLINE void via_ecb_op5( + const void *k, const void *c, const void *s, void *d, int l) +{ __asm + { + NEH_REKEY + mov ebx, (k) + mov edx, (c) + mov esi, (s) + mov edi, (d) + mov ecx, (l) + NEH_ECB + } +} + +INLINE void via_cbc_op6( + const void *k, const void *c, const void *s, void *d, int l, void *v) +{ __asm + { + NEH_REKEY + mov ebx, (k) + mov edx, (c) + mov esi, (s) + mov edi, (d) + mov ecx, (l) + mov eax, (v) + NEH_CBC + } +} + +INLINE void via_cbc_op7( + const void *k, const void *c, const void *s, void *d, int l, void *v, void *w) +{ __asm + { + NEH_REKEY + mov ebx, (k) + mov edx, (c) + mov esi, (s) + mov edi, (d) + mov ecx, (l) + mov eax, (v) + NEH_CBC + mov esi, eax + mov edi, (w) + movsd + movsd + movsd + movsd + } +} + +INLINE void via_cfb_op6( + const void *k, const void *c, const void *s, void *d, int l, void *v) +{ __asm + { + NEH_REKEY + mov ebx, (k) + mov edx, (c) + mov esi, (s) + mov edi, (d) + mov ecx, (l) + mov eax, (v) + NEH_CFB + } +} + +INLINE void via_cfb_op7( + const void *k, const void *c, const void *s, void *d, int l, void *v, void *w) +{ __asm + { + NEH_REKEY + mov ebx, (k) + mov edx, (c) + mov esi, (s) + mov edi, (d) + mov ecx, (l) + mov eax, (v) + NEH_CFB + mov esi, eax + mov edi, (w) + movsd + movsd + movsd + movsd + } +} + +INLINE void via_ofb_op6( + const void *k, const void *c, const void *s, void *d, int l, void *v) +{ __asm + { + NEH_REKEY + mov ebx, (k) + mov edx, (c) + mov esi, (s) + mov edi, (d) + mov ecx, (l) + mov eax, (v) + NEH_OFB + } +} + +#elif defined( __GNUC__ ) + +#define NEH_REKEY asm("pushfl\n popfl\n\t") +#define NEH_ECB asm(".byte 0xf3, 0x0f, 0xa7, 0xc8\n\t") +#define NEH_CBC asm(".byte 0xf3, 0x0f, 0xa7, 0xd0\n\t") +#define NEH_CFB asm(".byte 0xf3, 0x0f, 0xa7, 0xe0\n\t") +#define NEH_OFB asm(".byte 0xf3, 0x0f, 0xa7, 0xe8\n\t") +#define NEH_RNG asm(".byte 0x0f, 0xa7, 0xc0\n\t"); + +INLINE int has_cpuid(void) +{ int val; + asm("pushfl\n\t"); + asm("movl 0(%esp),%eax\n\t"); + asm("xor $0x00200000,%eax\n\t"); + asm("pushl %eax\n\t"); + asm("popfl\n\t"); + asm("pushfl\n\t"); + asm("popl %eax\n\t"); + asm("xorl 0(%esp),%edx\n\t"); + asm("andl $0x00200000,%eax\n\t"); + asm("movl %%eax,%0\n\t" : "=m" (val)); + asm("popfl\n\t"); + return val ? 1 : 0; +} + +INLINE int is_via_cpu(void) +{ int val; + asm("xorl %eax,%eax\n\t"); + asm("cpuid\n\t"); + asm("xorl %eax,%eax\n\t"); + asm("subl $0x746e6543,%ebx\n\t"); + asm("orl %ebx,%eax\n\t"); + asm("subl $0x48727561,%edx\n\t"); + asm("orl %edx,%eax\n\t"); + asm("subl $0x736c7561,%ecx\n\t"); + asm("orl %ecx,%eax\n\t"); + asm("movl %%eax,%0\n\t" : "=m" (val)); + val = (val ? 0 : 1); + via_flags = (val | NEH_CPU_READ); + return val; +} + +INLINE int read_via_flags(void) +{ unsigned char val; + asm("movl $0xc0000000,%eax\n\t"); + asm("cpuid\n\t"); + asm("movl $0xc0000001,%edx\n\t"); + asm("cmpl %edx,%eax\n\t"); + asm("setae %al\n\t"); + asm("movb %%al,%0\n\t" : "=m" (val)); + if(!val) return 0; + asm("movl $0xc0000001,%eax\n\t"); + asm("cpuid\n\t"); + asm("movb %%dl,%0\n\t" : "=m" (val)); + val &= NEH_FLAGS_MASK; + via_flags |= val; + return (int) val; +} + +INLINE int via_rng_in(void *buf) +{ int val; + asm("pushl %edi\n\t"); + asm("movl %0,%%edi\n\t" : : "m" (buf)); + asm("xorl %edx,%edx\n\t"); + NEH_RNG + asm("andl $0x0000001f,%eax\n\t"); + asm("movl %%eax,%0\n\t" : "=m" (val)); + asm("popl %edi\n\t"); + return val; +} + +INLINE volatile void via_ecb_op5( + const void *k, const void *c, const void *s, void *d, int l) +{ + NEH_REKEY; + asm("movl %0, %%ebx\n\t" : : "m" (k)); + asm("movl %0, %%edx\n\t" : : "m" (c)); + asm("movl %0, %%esi\n\t" : : "m" (s)); + asm("movl %0, %%edi\n\t" : : "m" (d)); + asm("movl %0, %%ecx\n\t" : : "m" (l)); + NEH_ECB; +} + +INLINE volatile void via_cbc_op6( + const void *k, const void *c, const void *s, void *d, int l, void *v) +{ + NEH_REKEY; + asm("movl %0, %%ebx\n\t" : : "m" (k)); + asm("movl %0, %%edx\n\t" : : "m" (c)); + asm("movl %0, %%esi\n\t" : : "m" (s)); + asm("movl %0, %%edi\n\t" : : "m" (d)); + asm("movl %0, %%ecx\n\t" : : "m" (l)); + asm("movl %0, %%eax\n\t" : : "m" (v)); + NEH_CBC; +} + +INLINE volatile void via_cbc_op7( + const void *k, const void *c, const void *s, void *d, int l, void *v, void *w) +{ + NEH_REKEY; + asm("movl %0, %%ebx\n\t" : : "m" (k)); + asm("movl %0, %%edx\n\t" : : "m" (c)); + asm("movl %0, %%esi\n\t" : : "m" (s)); + asm("movl %0, %%edi\n\t" : : "m" (d)); + asm("movl %0, %%ecx\n\t" : : "m" (l)); + asm("movl %0, %%eax\n\t" : : "m" (v)); + NEH_CBC; + asm("movl %eax,%esi\n\t"); + asm("movl %0, %%edi\n\t" : : "m" (w)); + asm("movsl; movsl; movsl; movsl\n\t"); +} + +INLINE volatile void via_cfb_op6( + const void *k, const void *c, const void *s, void *d, int l, void *v) +{ + NEH_REKEY; + asm("movl %0, %%ebx\n\t" : : "m" (k)); + asm("movl %0, %%edx\n\t" : : "m" (c)); + asm("movl %0, %%esi\n\t" : : "m" (s)); + asm("movl %0, %%edi\n\t" : : "m" (d)); + asm("movl %0, %%ecx\n\t" : : "m" (l)); + asm("movl %0, %%eax\n\t" : : "m" (v)); + NEH_CFB; +} + +INLINE volatile void via_cfb_op7( + const void *k, const void *c, const void *s, void *d, int l, void *v, void *w) +{ + NEH_REKEY; + asm("movl %0, %%ebx\n\t" : : "m" (k)); + asm("movl %0, %%edx\n\t" : : "m" (c)); + asm("movl %0, %%esi\n\t" : : "m" (s)); + asm("movl %0, %%edi\n\t" : : "m" (d)); + asm("movl %0, %%ecx\n\t" : : "m" (l)); + asm("movl %0, %%eax\n\t" : : "m" (v)); + NEH_CFB; + asm("movl %eax,%esi\n\t"); + asm("movl %0, %%edi\n\t" : : "m" (w)); + asm("movsl; movsl; movsl; movsl\n\t"); +} + +INLINE volatile void via_ofb_op6( + const void *k, const void *c, const void *s, void *d, int l, void *v) +{ + NEH_REKEY; + asm("movl %0, %%ebx\n\t" : : "m" (k)); + asm("movl %0, %%edx\n\t" : : "m" (c)); + asm("movl %0, %%esi\n\t" : : "m" (s)); + asm("movl %0, %%edi\n\t" : : "m" (d)); + asm("movl %0, %%ecx\n\t" : : "m" (l)); + asm("movl %0, %%eax\n\t" : : "m" (v)); + NEH_OFB; +} + +#else +#error VIA ACE is not available with this compiler +#endif + +INLINE int via_ace_test(void) +{ + return has_cpuid() && is_via_cpu() && ((read_via_flags() & NEH_ACE_FLAGS) == NEH_ACE_FLAGS); +} + +#define VIA_ACE_AVAILABLE (((via_flags & NEH_ACE_FLAGS) == NEH_ACE_FLAGS) \ + || (via_flags & NEH_CPU_READ) && (via_flags & NEH_CPU_IS_VIA) || via_ace_test()) + +INLINE int via_rng_test(void) +{ + return has_cpuid() && is_via_cpu() && ((read_via_flags() & NEH_RNG_FLAGS) == NEH_RNG_FLAGS); +} + +#define VIA_RNG_AVAILABLE (((via_flags & NEH_RNG_FLAGS) == NEH_RNG_FLAGS) \ + || (via_flags & NEH_CPU_READ) && (via_flags & NEH_CPU_IS_VIA) || via_rng_test()) + +INLINE int read_via_rng(void *buf, int count) +{ int nbr, max_reads, lcnt = count; + unsigned char *p, *q; + aligned_auto(unsigned char, bp, 64, 16); + + if(!VIA_RNG_AVAILABLE) + return 0; + + do + { + max_reads = MAX_READ_ATTEMPTS; + do + nbr = via_rng_in(bp); + while + (nbr == 0 && --max_reads); + + lcnt -= nbr; + p = (unsigned char*)buf; q = bp; + while(nbr--) + *p++ = *q++; + } + while + (lcnt && max_reads); + + return count - lcnt; +} + +#endif diff --git a/src/java/KP2AKdbLibrary/jni/aes/aes_x86_v1.asm b/src/java/KP2AKdbLibrary/jni/aes/aes_x86_v1.asm new file mode 100644 index 00000000..e1329263 --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/aes_x86_v1.asm @@ -0,0 +1,644 @@ + +; --------------------------------------------------------------------------- +; Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. +; +; LICENSE TERMS +; +; The redistribution and use of this software (with or without changes) +; is allowed without the payment of fees or royalties provided that: +; +; 1. source code distributions include the above copyright notice, this +; list of conditions and the following disclaimer; +; +; 2. binary distributions include the above copyright notice, this list +; of conditions and the following disclaimer in their documentation; +; +; 3. the name of the copyright holder is not used to endorse products +; built using this software without specific written permission. +; +; DISCLAIMER +; +; This software is provided 'as is' with no explicit or implied warranties +; in respect of its properties, including, but not limited to, correctness +; and/or fitness for purpose. +; --------------------------------------------------------------------------- +; Issue 13/08/2008 +; +; This code requires ASM_X86_V1C to be set in aesopt.h. It requires the C files +; aeskey.c and aestab.c for support. + +; An AES implementation for x86 processors using the YASM (or NASM) assembler. +; This is an assembler implementation that covers encryption and decryption +; only and is intended as a replacement of the C file aescrypt.c. It hence +; requires the file aeskey.c for keying and aestab.c for the AES tables. It +; employs full tables rather than compressed tables. + +; This code provides the standard AES block size (128 bits, 16 bytes) and the +; three standard AES key sizes (128, 192 and 256 bits). It has the same call +; interface as my C implementation. The ebx, esi, edi and ebp registers are +; preserved across calls but eax, ecx and edx and the artihmetic status flags +; are not. It is also important that the defines below match those used in the +; C code. This code uses the VC++ register saving conentions; if it is used +; with another compiler, conventions for using and saving registers may need to +; be checked (and calling conventions). The YASM command line for the VC++ +; custom build step is: +; +; yasm -Xvc -f win32 -o "$(TargetDir)\$(InputName).obj" "$(InputPath)" +; +; The calling intefaces are: +; +; AES_RETURN aes_encrypt(const unsigned char in_blk[], +; unsigned char out_blk[], const aes_encrypt_ctx cx[1]); +; +; AES_RETURN aes_decrypt(const unsigned char in_blk[], +; unsigned char out_blk[], const aes_decrypt_ctx cx[1]); +; +; AES_RETURN aes_encrypt_key(const unsigned char key[], +; const aes_encrypt_ctx cx[1]); +; +; AES_RETURN aes_decrypt_key(const unsigned char key[], +; const aes_decrypt_ctx cx[1]); +; +; AES_RETURN aes_encrypt_key(const unsigned char key[], +; unsigned int len, const aes_decrypt_ctx cx[1]); +; +; AES_RETURN aes_decrypt_key(const unsigned char key[], +; unsigned int len, const aes_decrypt_ctx cx[1]); +; +; where is 128, 102 or 256. In the last two calls the length can be in +; either bits or bytes. +; +; Comment in/out the following lines to obtain the desired subroutines. These +; selections MUST match those in the C header file aes.h + +%define AES_128 ; define if AES with 128 bit keys is needed +%define AES_192 ; define if AES with 192 bit keys is needed +%define AES_256 ; define if AES with 256 bit keys is needed +%define AES_VAR ; define if a variable key size is needed +%define ENCRYPTION ; define if encryption is needed +%define DECRYPTION ; define if decryption is needed +%define AES_REV_DKS ; define if key decryption schedule is reversed +%define LAST_ROUND_TABLES ; define if tables are to be used for last round + +; offsets to parameters + +in_blk equ 4 ; input byte array address parameter +out_blk equ 8 ; output byte array address parameter +ctx equ 12 ; AES context structure +stk_spc equ 20 ; stack space +%define parms 12 ; parameter space on stack + +; The encryption key schedule has the following in memory layout where N is the +; number of rounds (10, 12 or 14): +; +; lo: | input key (round 0) | ; each round is four 32-bit words +; | encryption round 1 | +; | encryption round 2 | +; .... +; | encryption round N-1 | +; hi: | encryption round N | +; +; The decryption key schedule is normally set up so that it has the same +; layout as above by actually reversing the order of the encryption key +; schedule in memory (this happens when AES_REV_DKS is set): +; +; lo: | decryption round 0 | = | encryption round N | +; | decryption round 1 | = INV_MIX_COL[ | encryption round N-1 | ] +; | decryption round 2 | = INV_MIX_COL[ | encryption round N-2 | ] +; .... .... +; | decryption round N-1 | = INV_MIX_COL[ | encryption round 1 | ] +; hi: | decryption round N | = | input key (round 0) | +; +; with rounds except the first and last modified using inv_mix_column() +; But if AES_REV_DKS is NOT set the order of keys is left as it is for +; encryption so that it has to be accessed in reverse when used for +; decryption (although the inverse mix column modifications are done) +; +; lo: | decryption round 0 | = | input key (round 0) | +; | decryption round 1 | = INV_MIX_COL[ | encryption round 1 | ] +; | decryption round 2 | = INV_MIX_COL[ | encryption round 2 | ] +; .... .... +; | decryption round N-1 | = INV_MIX_COL[ | encryption round N-1 | ] +; hi: | decryption round N | = | encryption round N | +; +; This layout is faster when the assembler key scheduling provided here +; is used. +; +; The DLL interface must use the _stdcall convention in which the number +; of bytes of parameter space is added after an @ to the sutine's name. +; We must also remove our parameters from the stack before return (see +; the do_exit macro). Define DLL_EXPORT for the Dynamic Link Library version. + +;%define DLL_EXPORT + +; End of user defines + +%ifdef AES_VAR +%ifndef AES_128 +%define AES_128 +%endif +%ifndef AES_192 +%define AES_192 +%endif +%ifndef AES_256 +%define AES_256 +%endif +%endif + +%ifdef AES_VAR +%define KS_LENGTH 60 +%elifdef AES_256 +%define KS_LENGTH 60 +%elifdef AES_192 +%define KS_LENGTH 52 +%else +%define KS_LENGTH 44 +%endif + +; These macros implement stack based local variables + +%macro save 2 + mov [esp+4*%1],%2 +%endmacro + +%macro restore 2 + mov %1,[esp+4*%2] +%endmacro + +; the DLL has to implement the _stdcall calling interface on return +; In this case we have to take our parameters (3 4-byte pointers) +; off the stack + +%macro do_name 1-2 parms +%ifndef DLL_EXPORT + global %1 +%1: +%else + global %1@%2 + export %1@%2 +%1@%2: +%endif +%endmacro + +%macro do_call 1-2 parms +%ifndef DLL_EXPORT + call %1 + add esp,%2 +%else + call %1@%2 +%endif +%endmacro + +%macro do_exit 0-1 parms +%ifdef DLL_EXPORT + ret %1 +%else + ret +%endif +%endmacro + +%ifdef ENCRYPTION + + extern _t_fn + +%define etab_0(x) [_t_fn+4*x] +%define etab_1(x) [_t_fn+1024+4*x] +%define etab_2(x) [_t_fn+2048+4*x] +%define etab_3(x) [_t_fn+3072+4*x] + +%ifdef LAST_ROUND_TABLES + + extern _t_fl + +%define eltab_0(x) [_t_fl+4*x] +%define eltab_1(x) [_t_fl+1024+4*x] +%define eltab_2(x) [_t_fl+2048+4*x] +%define eltab_3(x) [_t_fl+3072+4*x] + +%else + +%define etab_b(x) byte [_t_fn+3072+4*x] + +%endif + +; ROUND FUNCTION. Build column[2] on ESI and column[3] on EDI that have the +; round keys pre-loaded. Build column[0] in EBP and column[1] in EBX. +; +; Input: +; +; EAX column[0] +; EBX column[1] +; ECX column[2] +; EDX column[3] +; ESI column key[round][2] +; EDI column key[round][3] +; EBP scratch +; +; Output: +; +; EBP column[0] unkeyed +; EBX column[1] unkeyed +; ESI column[2] keyed +; EDI column[3] keyed +; EAX scratch +; ECX scratch +; EDX scratch + +%macro rnd_fun 2 + + rol ebx,16 + %1 esi, cl, 0, ebp + %1 esi, dh, 1, ebp + %1 esi, bh, 3, ebp + %1 edi, dl, 0, ebp + %1 edi, ah, 1, ebp + %1 edi, bl, 2, ebp + %2 ebp, al, 0, ebp + shr ebx,16 + and eax,0xffff0000 + or eax,ebx + shr edx,16 + %1 ebp, ah, 1, ebx + %1 ebp, dh, 3, ebx + %2 ebx, dl, 2, ebx + %1 ebx, ch, 1, edx + %1 ebx, al, 0, edx + shr eax,16 + shr ecx,16 + %1 ebp, cl, 2, edx + %1 edi, ch, 3, edx + %1 esi, al, 2, edx + %1 ebx, ah, 3, edx + +%endmacro + +; Basic MOV and XOR Operations for normal rounds + +%macro nr_xor 4 + movzx %4,%2 + xor %1,etab_%3(%4) +%endmacro + +%macro nr_mov 4 + movzx %4,%2 + mov %1,etab_%3(%4) +%endmacro + +; Basic MOV and XOR Operations for last round + +%ifdef LAST_ROUND_TABLES + + %macro lr_xor 4 + movzx %4,%2 + xor %1,eltab_%3(%4) + %endmacro + + %macro lr_mov 4 + movzx %4,%2 + mov %1,eltab_%3(%4) + %endmacro + +%else + + %macro lr_xor 4 + movzx %4,%2 + movzx %4,etab_b(%4) + %if %3 != 0 + shl %4,8*%3 + %endif + xor %1,%4 + %endmacro + + %macro lr_mov 4 + movzx %4,%2 + movzx %1,etab_b(%4) + %if %3 != 0 + shl %1,8*%3 + %endif + %endmacro + +%endif + +%macro enc_round 0 + + add ebp,16 + save 0,ebp + mov esi,[ebp+8] + mov edi,[ebp+12] + + rnd_fun nr_xor, nr_mov + + mov eax,ebp + mov ecx,esi + mov edx,edi + restore ebp,0 + xor eax,[ebp] + xor ebx,[ebp+4] + +%endmacro + +%macro enc_last_round 0 + + add ebp,16 + save 0,ebp + mov esi,[ebp+8] + mov edi,[ebp+12] + + rnd_fun lr_xor, lr_mov + + mov eax,ebp + restore ebp,0 + xor eax,[ebp] + xor ebx,[ebp+4] + +%endmacro + + section .text align=32 + +; AES Encryption Subroutine + + align 32 + do_name _aes_encrypt + + sub esp,stk_spc + mov [esp+16],ebp + mov [esp+12],ebx + mov [esp+ 8],esi + mov [esp+ 4],edi + + mov esi,[esp+in_blk+stk_spc] ; input pointer + mov eax,[esi ] + mov ebx,[esi+ 4] + mov ecx,[esi+ 8] + mov edx,[esi+12] + + mov ebp,[esp+ctx+stk_spc] ; key pointer + movzx edi,byte [ebp+4*KS_LENGTH] + xor eax,[ebp ] + xor ebx,[ebp+ 4] + xor ecx,[ebp+ 8] + xor edx,[ebp+12] + +; determine the number of rounds + + cmp edi,10*16 + je .3 + cmp edi,12*16 + je .2 + cmp edi,14*16 + je .1 + mov eax,-1 + jmp .5 + +.1: enc_round + enc_round +.2: enc_round + enc_round +.3: enc_round + enc_round + enc_round + enc_round + enc_round + enc_round + enc_round + enc_round + enc_round + enc_last_round + + mov edx,[esp+out_blk+stk_spc] + mov [edx],eax + mov [edx+4],ebx + mov [edx+8],esi + mov [edx+12],edi + xor eax,eax + +.5: mov ebp,[esp+16] + mov ebx,[esp+12] + mov esi,[esp+ 8] + mov edi,[esp+ 4] + add esp,stk_spc + do_exit + +%endif + +%ifdef DECRYPTION + + extern _t_in + +%define dtab_0(x) [_t_in+4*x] +%define dtab_1(x) [_t_in+1024+4*x] +%define dtab_2(x) [_t_in+2048+4*x] +%define dtab_3(x) [_t_in+3072+4*x] + +%ifdef LAST_ROUND_TABLES + + extern _t_il + +%define dltab_0(x) [_t_il+4*x] +%define dltab_1(x) [_t_il+1024+4*x] +%define dltab_2(x) [_t_il+2048+4*x] +%define dltab_3(x) [_t_il+3072+4*x] + +%else + + extern _t_ibox + +%define dtab_x(x) byte [_t_ibox+x] + +%endif + +%macro irn_fun 2 + + rol eax,16 + %1 esi, cl, 0, ebp + %1 esi, bh, 1, ebp + %1 esi, al, 2, ebp + %1 edi, dl, 0, ebp + %1 edi, ch, 1, ebp + %1 edi, ah, 3, ebp + %2 ebp, bl, 0, ebp + shr eax,16 + and ebx,0xffff0000 + or ebx,eax + shr ecx,16 + %1 ebp, bh, 1, eax + %1 ebp, ch, 3, eax + %2 eax, cl, 2, ecx + %1 eax, bl, 0, ecx + %1 eax, dh, 1, ecx + shr ebx,16 + shr edx,16 + %1 esi, dh, 3, ecx + %1 ebp, dl, 2, ecx + %1 eax, bh, 3, ecx + %1 edi, bl, 2, ecx + +%endmacro + +; Basic MOV and XOR Operations for normal rounds + +%macro ni_xor 4 + movzx %4,%2 + xor %1,dtab_%3(%4) +%endmacro + +%macro ni_mov 4 + movzx %4,%2 + mov %1,dtab_%3(%4) +%endmacro + +; Basic MOV and XOR Operations for last round + +%ifdef LAST_ROUND_TABLES + +%macro li_xor 4 + movzx %4,%2 + xor %1,dltab_%3(%4) +%endmacro + +%macro li_mov 4 + movzx %4,%2 + mov %1,dltab_%3(%4) +%endmacro + +%else + + %macro li_xor 4 + movzx %4,%2 + movzx %4,dtab_x(%4) + %if %3 != 0 + shl %4,8*%3 + %endif + xor %1,%4 + %endmacro + + %macro li_mov 4 + movzx %4,%2 + movzx %1,dtab_x(%4) + %if %3 != 0 + shl %1,8*%3 + %endif + %endmacro + +%endif + +%macro dec_round 0 + +%ifdef AES_REV_DKS + add ebp,16 +%else + sub ebp,16 +%endif + save 0,ebp + mov esi,[ebp+8] + mov edi,[ebp+12] + + irn_fun ni_xor, ni_mov + + mov ebx,ebp + mov ecx,esi + mov edx,edi + restore ebp,0 + xor eax,[ebp] + xor ebx,[ebp+4] + +%endmacro + +%macro dec_last_round 0 + +%ifdef AES_REV_DKS + add ebp,16 +%else + sub ebp,16 +%endif + save 0,ebp + mov esi,[ebp+8] + mov edi,[ebp+12] + + irn_fun li_xor, li_mov + + mov ebx,ebp + restore ebp,0 + xor eax,[ebp] + xor ebx,[ebp+4] + +%endmacro + + section .text + +; AES Decryption Subroutine + + align 32 + do_name _aes_decrypt + + sub esp,stk_spc + mov [esp+16],ebp + mov [esp+12],ebx + mov [esp+ 8],esi + mov [esp+ 4],edi + +; input four columns and xor in first round key + + mov esi,[esp+in_blk+stk_spc] ; input pointer + mov eax,[esi ] + mov ebx,[esi+ 4] + mov ecx,[esi+ 8] + mov edx,[esi+12] + lea esi,[esi+16] + + mov ebp,[esp+ctx+stk_spc] ; key pointer + movzx edi,byte[ebp+4*KS_LENGTH] +%ifndef AES_REV_DKS ; if decryption key schedule is not reversed + lea ebp,[ebp+edi] ; we have to access it from the top down +%endif + xor eax,[ebp ] ; key schedule + xor ebx,[ebp+ 4] + xor ecx,[ebp+ 8] + xor edx,[ebp+12] + +; determine the number of rounds + + cmp edi,10*16 + je .3 + cmp edi,12*16 + je .2 + cmp edi,14*16 + je .1 + mov eax,-1 + jmp .5 + +.1: dec_round + dec_round +.2: dec_round + dec_round +.3: dec_round + dec_round + dec_round + dec_round + dec_round + dec_round + dec_round + dec_round + dec_round + dec_last_round + +; move final values to the output array. + + mov ebp,[esp+out_blk+stk_spc] + mov [ebp],eax + mov [ebp+4],ebx + mov [ebp+8],esi + mov [ebp+12],edi + xor eax,eax + +.5: mov ebp,[esp+16] + mov ebx,[esp+12] + mov esi,[esp+ 8] + mov edi,[esp+ 4] + add esp,stk_spc + do_exit + +%endif + + end + diff --git a/src/java/KP2AKdbLibrary/jni/aes/aes_x86_v2.asm b/src/java/KP2AKdbLibrary/jni/aes/aes_x86_v2.asm new file mode 100644 index 00000000..ddceb259 --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/aes_x86_v2.asm @@ -0,0 +1,1419 @@ + +; --------------------------------------------------------------------------- +; Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. +; +; LICENSE TERMS +; +; The redistribution and use of this software (with or without changes) +; is allowed without the payment of fees or royalties provided that: +; +; 1. source code distributions include the above copyright notice, this +; list of conditions and the following disclaimer; +; +; 2. binary distributions include the above copyright notice, this list +; of conditions and the following disclaimer in their documentation; +; +; 3. the name of the copyright holder is not used to endorse products +; built using this software without specific written permission. +; +; DISCLAIMER +; +; This software is provided 'as is' with no explicit or implied warranties +; in respect of its properties, including, but not limited to, correctness +; and/or fitness for purpose. +; --------------------------------------------------------------------------- +; Issue 13/08/2008 +; +; This code requires either ASM_X86_V2 or ASM_X86_V2C to be set in aesopt.h +; and the same define to be set here as well. If AES_V2C is set this file +; requires the C files aeskey.c and aestab.c for support. + +; An AES implementation for x86 processors using the YASM (or NASM) assembler. +; This is a full assembler implementation covering encryption, decryption and +; key scheduling. It uses 2k bytes of tables but its encryption and decryption +; performance is very close to that obtained using large tables. Key schedule +; expansion is slower for both encryption and decryption but this is likely to +; be offset by the much smaller load that this version places on the processor +; cache. I acknowledge the contribution made by Daniel Bernstein to aspects of +; the design of the AES round function used here. +; +; This code provides the standard AES block size (128 bits, 16 bytes) and the +; three standard AES key sizes (128, 192 and 256 bits). It has the same call +; interface as my C implementation. The ebx, esi, edi and ebp registers are +; preserved across calls but eax, ecx and edx and the artihmetic status flags +; are not. Although this is a full assembler implementation, it can be used +; in conjunction with my C code which provides faster key scheduling using +; large tables. In this case aeskey.c should be compiled with ASM_X86_V2C +; defined. It is also important that the defines below match those used in the +; C code. This code uses the VC++ register saving conentions; if it is used +; with another compiler, conventions for using and saving registers may need +; to be checked (and calling conventions). The YASM command line for the VC++ +; custom build step is: +; +; yasm -Xvc -f win32 -D -o "$(TargetDir)\$(InputName).obj" "$(InputPath)" +; +; For the cryptlib build this is (pcg): +; +; yasm -Xvc -f win32 -D ASM_X86_V2C -o aescrypt2.obj aes_x86_v2.asm +; +; where is ASM_X86_V2 or ASM_X86_V2C. The calling intefaces are: +; +; AES_RETURN aes_encrypt(const unsigned char in_blk[], +; unsigned char out_blk[], const aes_encrypt_ctx cx[1]); +; +; AES_RETURN aes_decrypt(const unsigned char in_blk[], +; unsigned char out_blk[], const aes_decrypt_ctx cx[1]); +; +; AES_RETURN aes_encrypt_key(const unsigned char key[], +; const aes_encrypt_ctx cx[1]); +; +; AES_RETURN aes_decrypt_key(const unsigned char key[], +; const aes_decrypt_ctx cx[1]); +; +; AES_RETURN aes_encrypt_key(const unsigned char key[], +; unsigned int len, const aes_decrypt_ctx cx[1]); +; +; AES_RETURN aes_decrypt_key(const unsigned char key[], +; unsigned int len, const aes_decrypt_ctx cx[1]); +; +; where is 128, 102 or 256. In the last two calls the length can be in +; either bits or bytes. + +; The DLL interface must use the _stdcall convention in which the number +; of bytes of parameter space is added after an @ to the sutine's name. +; We must also remove our parameters from the stack before return (see +; the do_exit macro). Define DLL_EXPORT for the Dynamic Link Library version. + +;%define DLL_EXPORT + +; Comment in/out the following lines to obtain the desired subroutines. These +; selections MUST match those in the C header file aes.h + +; The size of the code can be reduced by using functions for the encryption +; and decryption rounds in place of macro expansion + +%define REDUCE_CODE_SIZE + +%define AES_128 ; define if AES with 128 bit keys is needed +%define AES_192 ; define if AES with 192 bit keys is needed +%define AES_256 ; define if AES with 256 bit keys is needed +%define AES_VAR ; define if a variable key size is needed +%define ENCRYPTION ; define if encryption is needed +%define DECRYPTION ; define if decryption is needed +%define AES_REV_DKS ; define if key decryption schedule is reversed + +%ifndef ASM_X86_V2C +%define ENCRYPTION_KEY_SCHEDULE ; define if encryption key expansion is needed +%define DECRYPTION_KEY_SCHEDULE ; define if decryption key expansion is needed +%endif + +; The encryption key schedule has the following in memory layout where N is the +; number of rounds (10, 12 or 14): +; +; lo: | input key (round 0) | ; each round is four 32-bit words +; | encryption round 1 | +; | encryption round 2 | +; .... +; | encryption round N-1 | +; hi: | encryption round N | +; +; The decryption key schedule is normally set up so that it has the same +; layout as above by actually reversing the order of the encryption key +; schedule in memory (this happens when AES_REV_DKS is set): +; +; lo: | decryption round 0 | = | encryption round N | +; | decryption round 1 | = INV_MIX_COL[ | encryption round N-1 | ] +; | decryption round 2 | = INV_MIX_COL[ | encryption round N-2 | ] +; .... .... +; | decryption round N-1 | = INV_MIX_COL[ | encryption round 1 | ] +; hi: | decryption round N | = | input key (round 0) | +; +; with rounds except the first and last modified using inv_mix_column() +; But if AES_REV_DKS is NOT set the order of keys is left as it is for +; encryption so that it has to be accessed in reverse when used for +; decryption (although the inverse mix column modifications are done) +; +; lo: | decryption round 0 | = | input key (round 0) | +; | decryption round 1 | = INV_MIX_COL[ | encryption round 1 | ] +; | decryption round 2 | = INV_MIX_COL[ | encryption round 2 | ] +; .... .... +; | decryption round N-1 | = INV_MIX_COL[ | encryption round N-1 | ] +; hi: | decryption round N | = | encryption round N | +; +; This layout is faster when the assembler key scheduling provided here +; is used. +; +; End of user defines + +%ifdef AES_VAR +%ifndef AES_128 +%define AES_128 +%endif +%ifndef AES_192 +%define AES_192 +%endif +%ifndef AES_256 +%define AES_256 +%endif +%endif + +%ifdef AES_VAR +%define KS_LENGTH 60 +%elifdef AES_256 +%define KS_LENGTH 60 +%elifdef AES_192 +%define KS_LENGTH 52 +%else +%define KS_LENGTH 44 +%endif + +%ifdef REDUCE_CODE_SIZE + %macro mf_call 1 + call %1 + %endmacro +%else + %macro mf_call 1 + %1 + %endmacro +%endif + +; the DLL has to implement the _stdcall calling interface on return +; In this case we have to take our parameters (3 4-byte pointers) +; off the stack + +%define parms 12 + +%macro do_name 1-2 parms +%ifndef DLL_EXPORT + global %1 +%1: +%else + global %1@%2 + export %1@%2 +%1@%2: +%endif +%endmacro + +%macro do_call 1-2 parms +%ifndef DLL_EXPORT + call %1 + add esp,%2 +%else + call %1@%2 +%endif +%endmacro + +%macro do_exit 0-1 parms +%ifdef DLL_EXPORT + ret %1 +%else + ret +%endif +%endmacro + +%ifndef ASM_X86_V2C + do_name _aes_init,0 + do_exit 0 +%endif + +; finite field multiplies by {02}, {04} and {08} + +%define f2(x) ((x<<1)^(((x>>7)&1)*0x11b)) +%define f4(x) ((x<<2)^(((x>>6)&1)*0x11b)^(((x>>6)&2)*0x11b)) +%define f8(x) ((x<<3)^(((x>>5)&1)*0x11b)^(((x>>5)&2)*0x11b)^(((x>>5)&4)*0x11b)) + +; finite field multiplies required in table generation + +%define f3(x) (f2(x) ^ x) +%define f9(x) (f8(x) ^ x) +%define fb(x) (f8(x) ^ f2(x) ^ x) +%define fd(x) (f8(x) ^ f4(x) ^ x) +%define fe(x) (f8(x) ^ f4(x) ^ f2(x)) + +%define etab_0(x) [enc_tab+4+8*x] +%define etab_1(x) [enc_tab+3+8*x] +%define etab_2(x) [enc_tab+2+8*x] +%define etab_3(x) [enc_tab+1+8*x] +%define etab_b(x) byte [enc_tab+1+8*x] ; used with movzx for 0x000000xx +%define etab_w(x) word [enc_tab+8*x] ; used with movzx for 0x0000xx00 + +%define btab_0(x) [enc_tab+6+8*x] +%define btab_1(x) [enc_tab+5+8*x] +%define btab_2(x) [enc_tab+4+8*x] +%define btab_3(x) [enc_tab+3+8*x] + +; ROUND FUNCTION. Build column[2] on ESI and column[3] on EDI that have the +; round keys pre-loaded. Build column[0] in EBP and column[1] in EBX. +; +; Input: +; +; EAX column[0] +; EBX column[1] +; ECX column[2] +; EDX column[3] +; ESI column key[round][2] +; EDI column key[round][3] +; EBP scratch +; +; Output: +; +; EBP column[0] unkeyed +; EBX column[1] unkeyed +; ESI column[2] keyed +; EDI column[3] keyed +; EAX scratch +; ECX scratch +; EDX scratch + +%macro rnd_fun 2 + + rol ebx,16 + %1 esi, cl, 0, ebp + %1 esi, dh, 1, ebp + %1 esi, bh, 3, ebp + %1 edi, dl, 0, ebp + %1 edi, ah, 1, ebp + %1 edi, bl, 2, ebp + %2 ebp, al, 0, ebp + shr ebx,16 + and eax,0xffff0000 + or eax,ebx + shr edx,16 + %1 ebp, ah, 1, ebx + %1 ebp, dh, 3, ebx + %2 ebx, dl, 2, ebx + %1 ebx, ch, 1, edx + %1 ebx, al, 0, edx + shr eax,16 + shr ecx,16 + %1 ebp, cl, 2, edx + %1 edi, ch, 3, edx + %1 esi, al, 2, edx + %1 ebx, ah, 3, edx + +%endmacro + +; Basic MOV and XOR Operations for normal rounds + +%macro nr_xor 4 + movzx %4,%2 + xor %1,etab_%3(%4) +%endmacro + +%macro nr_mov 4 + movzx %4,%2 + mov %1,etab_%3(%4) +%endmacro + +; Basic MOV and XOR Operations for last round + +%if 1 + + %macro lr_xor 4 + movzx %4,%2 + movzx %4,etab_b(%4) + %if %3 != 0 + shl %4,8*%3 + %endif + xor %1,%4 + %endmacro + + %macro lr_mov 4 + movzx %4,%2 + movzx %1,etab_b(%4) + %if %3 != 0 + shl %1,8*%3 + %endif + %endmacro + +%else ; less effective but worth leaving as an option + + %macro lr_xor 4 + movzx %4,%2 + mov %4,btab_%3(%4) + and %4,0x000000ff << 8 * %3 + xor %1,%4 + %endmacro + + %macro lr_mov 4 + movzx %4,%2 + mov %1,btab_%3(%4) + and %1,0x000000ff << 8 * %3 + %endmacro + +%endif + +; Apply S-Box to the 4 bytes in a 32-bit word and rotate byte positions + +%ifdef REDUCE_CODE_SIZE + + global _ls_sub +_ls_sub: ; ls_sub(t,n) = ls_box(t,n) + mov ecx,[esp+8] + mov eax,[esp+4] + shl ecx,3 + rol eax,cl + xor edx,edx +l3s_col: + movzx ecx,al ; in eax + movzx ecx, etab_b(ecx) ; out eax + xor edx,ecx ; scratch ecx,edx + movzx ecx,ah + movzx ecx, etab_b(ecx) + shl ecx,8 + xor edx,ecx + shr eax,16 + movzx ecx,al + movzx ecx, etab_b(ecx) + shl ecx,16 + xor edx,ecx + movzx ecx,ah + movzx ecx, etab_b(ecx) + shl ecx,24 + xor edx,ecx + mov eax,edx + ret + +%else + +%macro l3s_col 0 + + movzx ecx,al ; in eax + movzx ecx, etab_b(ecx) ; out eax + xor edx,ecx ; scratch ecx,edx + movzx ecx,ah + movzx ecx, etab_b(ecx) + shl ecx,8 + xor edx,ecx + shr eax,16 + movzx ecx,al + movzx ecx, etab_b(ecx) + shl ecx,16 + xor edx,ecx + movzx ecx,ah + movzx ecx, etab_b(ecx) + shl ecx,24 + xor edx,ecx + mov eax,edx + +%endmacro + +%endif + +; offsets to parameters + +in_blk equ 4 ; input byte array address parameter +out_blk equ 8 ; output byte array address parameter +ctx equ 12 ; AES context structure +stk_spc equ 16 ; stack space + +%ifdef ENCRYPTION + +%define ENCRYPTION_TABLE + +%ifdef REDUCE_CODE_SIZE + +enc_round: + add ebp,16 + mov esi,[ebp+8] + mov edi,[ebp+12] + push ebp + rnd_fun nr_xor, nr_mov + mov eax,ebp + pop ebp + mov ecx,esi + mov edx,edi + xor eax,[ebp] + xor ebx,[ebp+4] + ret + +%else + +%macro enc_round 0 + + add ebp,16 + mov esi,[ebp+8] + mov edi,[ebp+12] + push ebp + rnd_fun nr_xor, nr_mov + mov eax,ebp + pop ebp + mov ecx,esi + mov edx,edi + xor eax,[ebp] + xor ebx,[ebp+4] + +%endmacro + +%endif + +%macro enc_last_round 0 + + add ebp,16 + mov esi,[ebp+8] + mov edi,[ebp+12] + push ebp + rnd_fun lr_xor, lr_mov + mov eax,ebp + pop ebp + xor eax,[ebp] + xor ebx,[ebp+4] + +%endmacro + + section .text align=32 + +; AES Encryption Subroutine + + align 32 + do_name _aes_encrypt,12 + push ebp + push ebx + push esi + push edi + +; load the input block + + mov esi,[esp+in_blk+stk_spc] ; input pointer + mov eax,[esi ] + mov ebx,[esi+ 4] + mov ecx,[esi+ 8] + mov edx,[esi+12] + +; and xor in first key block + + mov ebp,[esp+ctx+stk_spc] ; key pointer + movzx edi,byte [ebp+4*KS_LENGTH] + xor eax,[ebp ] + xor ebx,[ebp+ 4] + xor ecx,[ebp+ 8] + xor edx,[ebp+12] + +; determine the number of rounds + + cmp edi,10*16 + je .3 + cmp edi,12*16 + je .2 + cmp edi,14*16 + je .1 + mov eax,-1 + jmp .5 + +; do the encryption rounds + +.1: mf_call enc_round + mf_call enc_round +.2: mf_call enc_round + mf_call enc_round +.3: mf_call enc_round + mf_call enc_round + mf_call enc_round + mf_call enc_round + mf_call enc_round + mf_call enc_round + mf_call enc_round + mf_call enc_round + mf_call enc_round + enc_last_round + +; output the block + + mov edx,[esp+out_blk+stk_spc] + mov [edx ],eax + mov [edx+ 4],ebx + mov [edx+ 8],esi + mov [edx+12],edi + xor eax,eax + +.5: pop edi + pop esi + pop ebx + pop ebp + do_exit 12 + +%endif + +%macro f_key 2 + + push ecx + push edx + mov edx,esi + ror eax,8 + mf_call l3s_col + mov esi,eax + pop edx + pop ecx + xor esi,rc_val + + mov [ebp+%1*%2],esi + xor edi,esi + mov [ebp+%1*%2+4],edi + xor ecx,edi + mov [ebp+%1*%2+8],ecx + xor edx,ecx + mov [ebp+%1*%2+12],edx + mov eax,edx + +%if %2 == 24 + +%if %1 < 7 + xor eax,[ebp+%1*%2+16-%2] + mov [ebp+%1*%2+16],eax + xor eax,[ebp+%1*%2+20-%2] + mov [ebp+%1*%2+20],eax +%endif + +%elif %2 == 32 + +%if %1 < 6 + push ecx + push edx + mov edx,[ebp+%1*%2+16-%2] + mf_call l3s_col + pop edx + pop ecx + mov [ebp+%1*%2+16],eax + xor eax,[ebp+%1*%2+20-%2] + mov [ebp+%1*%2+20],eax + xor eax,[ebp+%1*%2+24-%2] + mov [ebp+%1*%2+24],eax + xor eax,[ebp+%1*%2+28-%2] + mov [ebp+%1*%2+28],eax +%endif + +%endif + +%assign rc_val f2(rc_val) + +%endmacro + +%ifdef ENCRYPTION_KEY_SCHEDULE + +%ifdef AES_128 + +%ifndef ENCRYPTION_TABLE +%define ENCRYPTION_TABLE +%endif + +%assign rc_val 1 + + align 32 + do_name _aes_encrypt_key128,8 + push ebp + push ebx + push esi + push edi + + mov ebp,[esp+24] + mov [ebp+4*KS_LENGTH],dword 10*16 + mov ebx,[esp+20] + + mov esi,[ebx] + mov [ebp],esi + mov edi,[ebx+4] + mov [ebp+4],edi + mov ecx,[ebx+8] + mov [ebp+8],ecx + mov edx,[ebx+12] + mov [ebp+12],edx + add ebp,16 + mov eax,edx + + f_key 0,16 ; 11 * 4 = 44 unsigned longs + f_key 1,16 ; 4 + 4 * 10 generated = 44 + f_key 2,16 + f_key 3,16 + f_key 4,16 + f_key 5,16 + f_key 6,16 + f_key 7,16 + f_key 8,16 + f_key 9,16 + + pop edi + pop esi + pop ebx + pop ebp + xor eax,eax + do_exit 8 + +%endif + +%ifdef AES_192 + +%ifndef ENCRYPTION_TABLE +%define ENCRYPTION_TABLE +%endif + +%assign rc_val 1 + + align 32 + do_name _aes_encrypt_key192,8 + push ebp + push ebx + push esi + push edi + + mov ebp,[esp+24] + mov [ebp+4*KS_LENGTH],dword 12 * 16 + mov ebx,[esp+20] + + mov esi,[ebx] + mov [ebp],esi + mov edi,[ebx+4] + mov [ebp+4],edi + mov ecx,[ebx+8] + mov [ebp+8],ecx + mov edx,[ebx+12] + mov [ebp+12],edx + mov eax,[ebx+16] + mov [ebp+16],eax + mov eax,[ebx+20] + mov [ebp+20],eax + add ebp,24 + + f_key 0,24 ; 13 * 4 = 52 unsigned longs + f_key 1,24 ; 6 + 6 * 8 generated = 54 + f_key 2,24 + f_key 3,24 + f_key 4,24 + f_key 5,24 + f_key 6,24 + f_key 7,24 + + pop edi + pop esi + pop ebx + pop ebp + xor eax,eax + do_exit 8 + +%endif + +%ifdef AES_256 + +%ifndef ENCRYPTION_TABLE +%define ENCRYPTION_TABLE +%endif + +%assign rc_val 1 + + align 32 + do_name _aes_encrypt_key256,8 + push ebp + push ebx + push esi + push edi + + mov ebp,[esp+24] + mov [ebp+4*KS_LENGTH],dword 14 * 16 + mov ebx,[esp+20] + + mov esi,[ebx] + mov [ebp],esi + mov edi,[ebx+4] + mov [ebp+4],edi + mov ecx,[ebx+8] + mov [ebp+8],ecx + mov edx,[ebx+12] + mov [ebp+12],edx + mov eax,[ebx+16] + mov [ebp+16],eax + mov eax,[ebx+20] + mov [ebp+20],eax + mov eax,[ebx+24] + mov [ebp+24],eax + mov eax,[ebx+28] + mov [ebp+28],eax + add ebp,32 + + f_key 0,32 ; 15 * 4 = 60 unsigned longs + f_key 1,32 ; 8 + 8 * 7 generated = 64 + f_key 2,32 + f_key 3,32 + f_key 4,32 + f_key 5,32 + f_key 6,32 + + pop edi + pop esi + pop ebx + pop ebp + xor eax,eax + do_exit 8 + +%endif + +%ifdef AES_VAR + +%ifndef ENCRYPTION_TABLE +%define ENCRYPTION_TABLE +%endif + + align 32 + do_name _aes_encrypt_key,12 + + mov ecx,[esp+4] + mov eax,[esp+8] + mov edx,[esp+12] + push edx + push ecx + + cmp eax,16 + je .1 + cmp eax,128 + je .1 + + cmp eax,24 + je .2 + cmp eax,192 + je .2 + + cmp eax,32 + je .3 + cmp eax,256 + je .3 + mov eax,-1 + add esp,8 + do_exit 12 + +.1: do_call _aes_encrypt_key128,8 + do_exit 12 +.2: do_call _aes_encrypt_key192,8 + do_exit 12 +.3: do_call _aes_encrypt_key256,8 + do_exit 12 + +%endif + +%endif + +%ifdef ENCRYPTION_TABLE + +; S-box data - 256 entries + + section .data align=32 + align 32 + +%define u8(x) 0, x, x, f3(x), f2(x), x, x, f3(x) + +enc_tab: + db u8(0x63),u8(0x7c),u8(0x77),u8(0x7b),u8(0xf2),u8(0x6b),u8(0x6f),u8(0xc5) + db u8(0x30),u8(0x01),u8(0x67),u8(0x2b),u8(0xfe),u8(0xd7),u8(0xab),u8(0x76) + db u8(0xca),u8(0x82),u8(0xc9),u8(0x7d),u8(0xfa),u8(0x59),u8(0x47),u8(0xf0) + db u8(0xad),u8(0xd4),u8(0xa2),u8(0xaf),u8(0x9c),u8(0xa4),u8(0x72),u8(0xc0) + db u8(0xb7),u8(0xfd),u8(0x93),u8(0x26),u8(0x36),u8(0x3f),u8(0xf7),u8(0xcc) + db u8(0x34),u8(0xa5),u8(0xe5),u8(0xf1),u8(0x71),u8(0xd8),u8(0x31),u8(0x15) + db u8(0x04),u8(0xc7),u8(0x23),u8(0xc3),u8(0x18),u8(0x96),u8(0x05),u8(0x9a) + db u8(0x07),u8(0x12),u8(0x80),u8(0xe2),u8(0xeb),u8(0x27),u8(0xb2),u8(0x75) + db u8(0x09),u8(0x83),u8(0x2c),u8(0x1a),u8(0x1b),u8(0x6e),u8(0x5a),u8(0xa0) + db u8(0x52),u8(0x3b),u8(0xd6),u8(0xb3),u8(0x29),u8(0xe3),u8(0x2f),u8(0x84) + db u8(0x53),u8(0xd1),u8(0x00),u8(0xed),u8(0x20),u8(0xfc),u8(0xb1),u8(0x5b) + db u8(0x6a),u8(0xcb),u8(0xbe),u8(0x39),u8(0x4a),u8(0x4c),u8(0x58),u8(0xcf) + db u8(0xd0),u8(0xef),u8(0xaa),u8(0xfb),u8(0x43),u8(0x4d),u8(0x33),u8(0x85) + db u8(0x45),u8(0xf9),u8(0x02),u8(0x7f),u8(0x50),u8(0x3c),u8(0x9f),u8(0xa8) + db u8(0x51),u8(0xa3),u8(0x40),u8(0x8f),u8(0x92),u8(0x9d),u8(0x38),u8(0xf5) + db u8(0xbc),u8(0xb6),u8(0xda),u8(0x21),u8(0x10),u8(0xff),u8(0xf3),u8(0xd2) + db u8(0xcd),u8(0x0c),u8(0x13),u8(0xec),u8(0x5f),u8(0x97),u8(0x44),u8(0x17) + db u8(0xc4),u8(0xa7),u8(0x7e),u8(0x3d),u8(0x64),u8(0x5d),u8(0x19),u8(0x73) + db u8(0x60),u8(0x81),u8(0x4f),u8(0xdc),u8(0x22),u8(0x2a),u8(0x90),u8(0x88) + db u8(0x46),u8(0xee),u8(0xb8),u8(0x14),u8(0xde),u8(0x5e),u8(0x0b),u8(0xdb) + db u8(0xe0),u8(0x32),u8(0x3a),u8(0x0a),u8(0x49),u8(0x06),u8(0x24),u8(0x5c) + db u8(0xc2),u8(0xd3),u8(0xac),u8(0x62),u8(0x91),u8(0x95),u8(0xe4),u8(0x79) + db u8(0xe7),u8(0xc8),u8(0x37),u8(0x6d),u8(0x8d),u8(0xd5),u8(0x4e),u8(0xa9) + db u8(0x6c),u8(0x56),u8(0xf4),u8(0xea),u8(0x65),u8(0x7a),u8(0xae),u8(0x08) + db u8(0xba),u8(0x78),u8(0x25),u8(0x2e),u8(0x1c),u8(0xa6),u8(0xb4),u8(0xc6) + db u8(0xe8),u8(0xdd),u8(0x74),u8(0x1f),u8(0x4b),u8(0xbd),u8(0x8b),u8(0x8a) + db u8(0x70),u8(0x3e),u8(0xb5),u8(0x66),u8(0x48),u8(0x03),u8(0xf6),u8(0x0e) + db u8(0x61),u8(0x35),u8(0x57),u8(0xb9),u8(0x86),u8(0xc1),u8(0x1d),u8(0x9e) + db u8(0xe1),u8(0xf8),u8(0x98),u8(0x11),u8(0x69),u8(0xd9),u8(0x8e),u8(0x94) + db u8(0x9b),u8(0x1e),u8(0x87),u8(0xe9),u8(0xce),u8(0x55),u8(0x28),u8(0xdf) + db u8(0x8c),u8(0xa1),u8(0x89),u8(0x0d),u8(0xbf),u8(0xe6),u8(0x42),u8(0x68) + db u8(0x41),u8(0x99),u8(0x2d),u8(0x0f),u8(0xb0),u8(0x54),u8(0xbb),u8(0x16) + +%endif + +%ifdef DECRYPTION + +%define DECRYPTION_TABLE + +%define dtab_0(x) [dec_tab+ 8*x] +%define dtab_1(x) [dec_tab+3+8*x] +%define dtab_2(x) [dec_tab+2+8*x] +%define dtab_3(x) [dec_tab+1+8*x] +%define dtab_x(x) byte [dec_tab+7+8*x] + +%macro irn_fun 2 + + rol eax,16 + %1 esi, cl, 0, ebp + %1 esi, bh, 1, ebp + %1 esi, al, 2, ebp + %1 edi, dl, 0, ebp + %1 edi, ch, 1, ebp + %1 edi, ah, 3, ebp + %2 ebp, bl, 0, ebp + shr eax,16 + and ebx,0xffff0000 + or ebx,eax + shr ecx,16 + %1 ebp, bh, 1, eax + %1 ebp, ch, 3, eax + %2 eax, cl, 2, ecx + %1 eax, bl, 0, ecx + %1 eax, dh, 1, ecx + shr ebx,16 + shr edx,16 + %1 esi, dh, 3, ecx + %1 ebp, dl, 2, ecx + %1 eax, bh, 3, ecx + %1 edi, bl, 2, ecx + +%endmacro + +; Basic MOV and XOR Operations for normal rounds + +%macro ni_xor 4 + movzx %4,%2 + xor %1,dtab_%3(%4) +%endmacro + +%macro ni_mov 4 + movzx %4,%2 + mov %1,dtab_%3(%4) +%endmacro + +; Basic MOV and XOR Operations for last round + +%macro li_xor 4 + movzx %4,%2 + movzx %4,dtab_x(%4) +%if %3 != 0 + shl %4,8*%3 +%endif + xor %1,%4 +%endmacro + +%macro li_mov 4 + movzx %4,%2 + movzx %1,dtab_x(%4) +%if %3 != 0 + shl %1,8*%3 +%endif +%endmacro + +%ifdef REDUCE_CODE_SIZE + +dec_round: +%ifdef AES_REV_DKS + add ebp,16 +%else + sub ebp,16 +%endif + mov esi,[ebp+8] + mov edi,[ebp+12] + push ebp + irn_fun ni_xor, ni_mov + mov ebx,ebp + pop ebp + mov ecx,esi + mov edx,edi + xor eax,[ebp] + xor ebx,[ebp+4] + ret + +%else + +%macro dec_round 0 + +%ifdef AES_REV_DKS + add ebp,16 +%else + sub ebp,16 +%endif + mov esi,[ebp+8] + mov edi,[ebp+12] + push ebp + irn_fun ni_xor, ni_mov + mov ebx,ebp + pop ebp + mov ecx,esi + mov edx,edi + xor eax,[ebp] + xor ebx,[ebp+4] + +%endmacro + +%endif + +%macro dec_last_round 0 + +%ifdef AES_REV_DKS + add ebp,16 +%else + sub ebp,16 +%endif + mov esi,[ebp+8] + mov edi,[ebp+12] + push ebp + irn_fun li_xor, li_mov + mov ebx,ebp + pop ebp + xor eax,[ebp] + xor ebx,[ebp+4] + +%endmacro + + section .text + +; AES Decryption Subroutine + + align 32 + do_name _aes_decrypt,12 + push ebp + push ebx + push esi + push edi + +; load the input block + + mov esi,[esp+in_blk+stk_spc] ; input pointer + mov eax,[esi ] + mov ebx,[esi+ 4] + mov ecx,[esi+ 8] + mov edx,[esi+12] + lea esi,[esi+16] + +; xor in the first round key + + mov ebp,[esp+ctx+stk_spc] ; key pointer + movzx edi,byte[ebp+4*KS_LENGTH] +%ifndef AES_REV_DKS ; if decryption key schedule is not reversed + lea ebp,[ebp+edi] ; we have to access it from the top down +%endif + xor eax,[ebp ] ; key schedule + xor ebx,[ebp+ 4] + xor ecx,[ebp+ 8] + xor edx,[ebp+12] + +; determine the number of rounds + + cmp edi,10*16 + je .3 + cmp edi,12*16 + je .2 + cmp edi,14*16 + je .1 + mov eax,-1 + jmp .5 + +; perform the decryption rounds + +.1: mf_call dec_round + mf_call dec_round +.2: mf_call dec_round + mf_call dec_round +.3: mf_call dec_round + mf_call dec_round + mf_call dec_round + mf_call dec_round + mf_call dec_round + mf_call dec_round + mf_call dec_round + mf_call dec_round + mf_call dec_round + dec_last_round + +; output the block + + mov ebp,[esp+out_blk+stk_spc] + mov [ebp ],eax + mov [ebp+ 4],ebx + mov [ebp+ 8],esi + mov [ebp+12],edi + xor eax,eax + +.5: pop edi + pop esi + pop ebx + pop ebp + do_exit 12 + +%endif + +%ifdef REDUCE_CODE_SIZE + + global _im_sub +_im_sub: + mov edx,[esp+4] +inv_mix_col: + movzx ecx,dl ; input eax, edx + movzx ecx,etab_b(ecx) ; output eax + mov eax,dtab_0(ecx) ; used ecx + movzx ecx,dh + shr edx,16 + movzx ecx,etab_b(ecx) + xor eax,dtab_1(ecx) + movzx ecx,dl + movzx ecx,etab_b(ecx) + xor eax,dtab_2(ecx) + movzx ecx,dh + movzx ecx,etab_b(ecx) + xor eax,dtab_3(ecx) + ret + +%else + +%macro inv_mix_col 0 + + movzx ecx,dl ; input eax, edx + movzx ecx,etab_b(ecx) ; output eax + mov eax,dtab_0(ecx) ; used ecx + movzx ecx,dh + shr edx,16 + movzx ecx,etab_b(ecx) + xor eax,dtab_1(ecx) + movzx ecx,dl + movzx ecx,etab_b(ecx) + xor eax,dtab_2(ecx) + movzx ecx,dh + movzx ecx,etab_b(ecx) + xor eax,dtab_3(ecx) + +%endmacro + +%endif + +%ifdef DECRYPTION_KEY_SCHEDULE + +%ifdef AES_128 + +%ifndef DECRYPTION_TABLE +%define DECRYPTION_TABLE +%endif + + align 32 + do_name _aes_decrypt_key128,8 + push ebp + push ebx + push esi + push edi + + mov eax,[esp+24] ; context + mov edx,[esp+20] ; key + push eax + push edx + do_call _aes_encrypt_key128,8 ; generate expanded encryption key + mov eax,10*16 + mov esi,[esp+24] ; pointer to first round key + lea edi,[esi+eax] ; pointer to last round key + add esi,32 + ; the inverse mix column transformation + mov edx,[esi-16] ; needs to be applied to all round keys + mf_call inv_mix_col ; except first and last. Hence start by + mov [esi-16],eax ; transforming the four sub-keys in the + mov edx,[esi-12] ; second round key + mf_call inv_mix_col + mov [esi-12],eax ; transformations for subsequent rounds + mov edx,[esi-8] ; can then be made more efficient by + mf_call inv_mix_col ; noting that for three of the four sub-keys + mov [esi-8],eax ; in the encryption round key ek[r]: + mov edx,[esi-4] ; + mf_call inv_mix_col ; ek[r][n] = ek[r][n-1] ^ ek[r-1][n] + mov [esi-4],eax ; + ; where n is 1..3. Hence the corresponding +.0: mov edx,[esi] ; subkeys in the decryption round key dk[r] + mf_call inv_mix_col ; also obey since inv_mix_col is linear in + mov [esi],eax ; GF(256): + xor eax,[esi-12] ; + mov [esi+4],eax ; dk[r][n] = dk[r][n-1] ^ dk[r-1][n] + xor eax,[esi-8] ; + mov [esi+8],eax ; So we only need one inverse mix column + xor eax,[esi-4] ; operation (n = 0) for each four word cycle + mov [esi+12],eax ; in the expanded key. + add esi,16 + cmp edi,esi + jg .0 + jmp dec_end + +%endif + +%ifdef AES_192 + +%ifndef DECRYPTION_TABLE +%define DECRYPTION_TABLE +%endif + + align 32 + do_name _aes_decrypt_key192,8 + push ebp + push ebx + push esi + push edi + + mov eax,[esp+24] ; context + mov edx,[esp+20] ; key + push eax + push edx + do_call _aes_encrypt_key192,8 ; generate expanded encryption key + mov eax,12*16 + mov esi,[esp+24] ; first round key + lea edi,[esi+eax] ; last round key + add esi,48 ; the first 6 words are the key, of + ; which the top 2 words are part of + mov edx,[esi-32] ; the second round key and hence + mf_call inv_mix_col ; need to be modified. After this we + mov [esi-32],eax ; need to do a further six values prior + mov edx,[esi-28] ; to using a more efficient technique + mf_call inv_mix_col ; based on: + mov [esi-28],eax ; + ; dk[r][n] = dk[r][n-1] ^ dk[r-1][n] + mov edx,[esi-24] ; + mf_call inv_mix_col ; for n = 1 .. 5 where the key expansion + mov [esi-24],eax ; cycle is now 6 words long + mov edx,[esi-20] + mf_call inv_mix_col + mov [esi-20],eax + mov edx,[esi-16] + mf_call inv_mix_col + mov [esi-16],eax + mov edx,[esi-12] + mf_call inv_mix_col + mov [esi-12],eax + mov edx,[esi-8] + mf_call inv_mix_col + mov [esi-8],eax + mov edx,[esi-4] + mf_call inv_mix_col + mov [esi-4],eax + +.0: mov edx,[esi] ; the expanded key is 13 * 4 = 44 32-bit words + mf_call inv_mix_col ; of which 11 * 4 = 44 have to be modified + mov [esi],eax ; using inv_mix_col. We have already done 8 + xor eax,[esi-20] ; of these so 36 are left - hence we need + mov [esi+4],eax ; exactly 6 loops of six here + xor eax,[esi-16] + mov [esi+8],eax + xor eax,[esi-12] + mov [esi+12],eax + xor eax,[esi-8] + mov [esi+16],eax + xor eax,[esi-4] + mov [esi+20],eax + add esi,24 + cmp edi,esi + jg .0 + jmp dec_end + +%endif + +%ifdef AES_256 + +%ifndef DECRYPTION_TABLE +%define DECRYPTION_TABLE +%endif + + align 32 + do_name _aes_decrypt_key256,8 + push ebp + push ebx + push esi + push edi + + mov eax,[esp+24] + mov edx,[esp+20] + push eax + push edx + do_call _aes_encrypt_key256,8 ; generate expanded encryption key + mov eax,14*16 + mov esi,[esp+24] + lea edi,[esi+eax] + add esi,64 + + mov edx,[esi-48] ; the primary key is 8 words, of which + mf_call inv_mix_col ; the top four require modification + mov [esi-48],eax + mov edx,[esi-44] + mf_call inv_mix_col + mov [esi-44],eax + mov edx,[esi-40] + mf_call inv_mix_col + mov [esi-40],eax + mov edx,[esi-36] + mf_call inv_mix_col + mov [esi-36],eax + + mov edx,[esi-32] ; the encryption key expansion cycle is + mf_call inv_mix_col ; now eight words long so we need to + mov [esi-32],eax ; start by doing one complete block + mov edx,[esi-28] + mf_call inv_mix_col + mov [esi-28],eax + mov edx,[esi-24] + mf_call inv_mix_col + mov [esi-24],eax + mov edx,[esi-20] + mf_call inv_mix_col + mov [esi-20],eax + mov edx,[esi-16] + mf_call inv_mix_col + mov [esi-16],eax + mov edx,[esi-12] + mf_call inv_mix_col + mov [esi-12],eax + mov edx,[esi-8] + mf_call inv_mix_col + mov [esi-8],eax + mov edx,[esi-4] + mf_call inv_mix_col + mov [esi-4],eax + +.0: mov edx,[esi] ; we can now speed up the remaining + mf_call inv_mix_col ; rounds by using the technique + mov [esi],eax ; outlined earlier. But note that + xor eax,[esi-28] ; there is one extra inverse mix + mov [esi+4],eax ; column operation as the 256 bit + xor eax,[esi-24] ; key has an extra non-linear step + mov [esi+8],eax ; for the midway element. + xor eax,[esi-20] + mov [esi+12],eax ; the expanded key is 15 * 4 = 60 + mov edx,[esi+16] ; 32-bit words of which 52 need to + mf_call inv_mix_col ; be modified. We have already done + mov [esi+16],eax ; 12 so 40 are left - which means + xor eax,[esi-12] ; that we need exactly 5 loops of 8 + mov [esi+20],eax + xor eax,[esi-8] + mov [esi+24],eax + xor eax,[esi-4] + mov [esi+28],eax + add esi,32 + cmp edi,esi + jg .0 + +%endif + +dec_end: + +%ifdef AES_REV_DKS + + mov esi,[esp+24] ; this reverses the order of the +.1: mov eax,[esi] ; round keys if required + mov ebx,[esi+4] + mov ebp,[edi] + mov edx,[edi+4] + mov [esi],ebp + mov [esi+4],edx + mov [edi],eax + mov [edi+4],ebx + + mov eax,[esi+8] + mov ebx,[esi+12] + mov ebp,[edi+8] + mov edx,[edi+12] + mov [esi+8],ebp + mov [esi+12],edx + mov [edi+8],eax + mov [edi+12],ebx + + add esi,16 + sub edi,16 + cmp edi,esi + jg .1 + +%endif + + pop edi + pop esi + pop ebx + pop ebp + xor eax,eax + do_exit 8 + +%ifdef AES_VAR + + align 32 + do_name _aes_decrypt_key,12 + + mov ecx,[esp+4] + mov eax,[esp+8] + mov edx,[esp+12] + push edx + push ecx + + cmp eax,16 + je .1 + cmp eax,128 + je .1 + + cmp eax,24 + je .2 + cmp eax,192 + je .2 + + cmp eax,32 + je .3 + cmp eax,256 + je .3 + mov eax,-1 + add esp,8 + do_exit 12 + +.1: do_call _aes_decrypt_key128,8 + do_exit 12 +.2: do_call _aes_decrypt_key192,8 + do_exit 12 +.3: do_call _aes_decrypt_key256,8 + do_exit 12 + +%endif + +%endif + +%ifdef DECRYPTION_TABLE + +; Inverse S-box data - 256 entries + + section .data + align 32 + +%define v8(x) fe(x), f9(x), fd(x), fb(x), fe(x), f9(x), fd(x), x + +dec_tab: + db v8(0x52),v8(0x09),v8(0x6a),v8(0xd5),v8(0x30),v8(0x36),v8(0xa5),v8(0x38) + db v8(0xbf),v8(0x40),v8(0xa3),v8(0x9e),v8(0x81),v8(0xf3),v8(0xd7),v8(0xfb) + db v8(0x7c),v8(0xe3),v8(0x39),v8(0x82),v8(0x9b),v8(0x2f),v8(0xff),v8(0x87) + db v8(0x34),v8(0x8e),v8(0x43),v8(0x44),v8(0xc4),v8(0xde),v8(0xe9),v8(0xcb) + db v8(0x54),v8(0x7b),v8(0x94),v8(0x32),v8(0xa6),v8(0xc2),v8(0x23),v8(0x3d) + db v8(0xee),v8(0x4c),v8(0x95),v8(0x0b),v8(0x42),v8(0xfa),v8(0xc3),v8(0x4e) + db v8(0x08),v8(0x2e),v8(0xa1),v8(0x66),v8(0x28),v8(0xd9),v8(0x24),v8(0xb2) + db v8(0x76),v8(0x5b),v8(0xa2),v8(0x49),v8(0x6d),v8(0x8b),v8(0xd1),v8(0x25) + db v8(0x72),v8(0xf8),v8(0xf6),v8(0x64),v8(0x86),v8(0x68),v8(0x98),v8(0x16) + db v8(0xd4),v8(0xa4),v8(0x5c),v8(0xcc),v8(0x5d),v8(0x65),v8(0xb6),v8(0x92) + db v8(0x6c),v8(0x70),v8(0x48),v8(0x50),v8(0xfd),v8(0xed),v8(0xb9),v8(0xda) + db v8(0x5e),v8(0x15),v8(0x46),v8(0x57),v8(0xa7),v8(0x8d),v8(0x9d),v8(0x84) + db v8(0x90),v8(0xd8),v8(0xab),v8(0x00),v8(0x8c),v8(0xbc),v8(0xd3),v8(0x0a) + db v8(0xf7),v8(0xe4),v8(0x58),v8(0x05),v8(0xb8),v8(0xb3),v8(0x45),v8(0x06) + db v8(0xd0),v8(0x2c),v8(0x1e),v8(0x8f),v8(0xca),v8(0x3f),v8(0x0f),v8(0x02) + db v8(0xc1),v8(0xaf),v8(0xbd),v8(0x03),v8(0x01),v8(0x13),v8(0x8a),v8(0x6b) + db v8(0x3a),v8(0x91),v8(0x11),v8(0x41),v8(0x4f),v8(0x67),v8(0xdc),v8(0xea) + db v8(0x97),v8(0xf2),v8(0xcf),v8(0xce),v8(0xf0),v8(0xb4),v8(0xe6),v8(0x73) + db v8(0x96),v8(0xac),v8(0x74),v8(0x22),v8(0xe7),v8(0xad),v8(0x35),v8(0x85) + db v8(0xe2),v8(0xf9),v8(0x37),v8(0xe8),v8(0x1c),v8(0x75),v8(0xdf),v8(0x6e) + db v8(0x47),v8(0xf1),v8(0x1a),v8(0x71),v8(0x1d),v8(0x29),v8(0xc5),v8(0x89) + db v8(0x6f),v8(0xb7),v8(0x62),v8(0x0e),v8(0xaa),v8(0x18),v8(0xbe),v8(0x1b) + db v8(0xfc),v8(0x56),v8(0x3e),v8(0x4b),v8(0xc6),v8(0xd2),v8(0x79),v8(0x20) + db v8(0x9a),v8(0xdb),v8(0xc0),v8(0xfe),v8(0x78),v8(0xcd),v8(0x5a),v8(0xf4) + db v8(0x1f),v8(0xdd),v8(0xa8),v8(0x33),v8(0x88),v8(0x07),v8(0xc7),v8(0x31) + db v8(0xb1),v8(0x12),v8(0x10),v8(0x59),v8(0x27),v8(0x80),v8(0xec),v8(0x5f) + db v8(0x60),v8(0x51),v8(0x7f),v8(0xa9),v8(0x19),v8(0xb5),v8(0x4a),v8(0x0d) + db v8(0x2d),v8(0xe5),v8(0x7a),v8(0x9f),v8(0x93),v8(0xc9),v8(0x9c),v8(0xef) + db v8(0xa0),v8(0xe0),v8(0x3b),v8(0x4d),v8(0xae),v8(0x2a),v8(0xf5),v8(0xb0) + db v8(0xc8),v8(0xeb),v8(0xbb),v8(0x3c),v8(0x83),v8(0x53),v8(0x99),v8(0x61) + db v8(0x17),v8(0x2b),v8(0x04),v8(0x7e),v8(0xba),v8(0x77),v8(0xd6),v8(0x26) + db v8(0xe1),v8(0x69),v8(0x14),v8(0x63),v8(0x55),v8(0x21),v8(0x0c),v8(0x7d) + +%endif + + end + diff --git a/src/java/KP2AKdbLibrary/jni/aes/aescpp.h b/src/java/KP2AKdbLibrary/jni/aes/aescpp.h new file mode 100644 index 00000000..291b4e09 --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/aescpp.h @@ -0,0 +1,148 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 20/12/2007 + + This file contains the definitions required to use AES (Rijndael) in C++. +*/ + +#ifndef _AESCPP_H +#define _AESCPP_H + +#include "aes.h" + +#if defined( AES_ENCRYPT ) + +class AESencrypt +{ +public: + aes_encrypt_ctx cx[1]; + AESencrypt(void) { aes_init(); }; +#if defined(AES_128) + AESencrypt(const unsigned char key[]) + { aes_encrypt_key128(key, cx); } + AES_RETURN key128(const unsigned char key[]) + { return aes_encrypt_key128(key, cx); } +#endif +#if defined(AES_192) + AES_RETURN key192(const unsigned char key[]) + { return aes_encrypt_key192(key, cx); } +#endif +#if defined(AES_256) + AES_RETURN key256(const unsigned char key[]) + { return aes_encrypt_key256(key, cx); } +#endif +#if defined(AES_VAR) + AES_RETURN key(const unsigned char key[], int key_len) + { return aes_encrypt_key(key, key_len, cx); } +#endif + AES_RETURN encrypt(const unsigned char in[], unsigned char out[]) const + { return aes_encrypt(in, out, cx); } +#ifndef AES_MODES + AES_RETURN ecb_encrypt(const unsigned char in[], unsigned char out[], int nb) const + { while(nb--) + { aes_encrypt(in, out, cx), in += AES_BLOCK_SIZE, out += AES_BLOCK_SIZE; } + } +#endif +#ifdef AES_MODES + AES_RETURN mode_reset(void) { return aes_mode_reset(cx); } + + AES_RETURN ecb_encrypt(const unsigned char in[], unsigned char out[], int nb) const + { return aes_ecb_encrypt(in, out, nb, cx); } + + AES_RETURN cbc_encrypt(const unsigned char in[], unsigned char out[], int nb, + unsigned char iv[]) const + { return aes_cbc_encrypt(in, out, nb, iv, cx); } + + AES_RETURN cfb_encrypt(const unsigned char in[], unsigned char out[], int nb, + unsigned char iv[]) + { return aes_cfb_encrypt(in, out, nb, iv, cx); } + + AES_RETURN cfb_decrypt(const unsigned char in[], unsigned char out[], int nb, + unsigned char iv[]) + { return aes_cfb_decrypt(in, out, nb, iv, cx); } + + AES_RETURN ofb_crypt(const unsigned char in[], unsigned char out[], int nb, + unsigned char iv[]) + { return aes_ofb_crypt(in, out, nb, iv, cx); } + + typedef void ctr_fn(unsigned char ctr[]); + + AES_RETURN ctr_crypt(const unsigned char in[], unsigned char out[], int nb, + unsigned char iv[], ctr_fn cf) + { return aes_ctr_crypt(in, out, nb, iv, cf, cx); } + +#endif + +}; + +#endif + +#if defined( AES_DECRYPT ) + +class AESdecrypt +{ +public: + aes_decrypt_ctx cx[1]; + AESdecrypt(void) { aes_init(); }; +#if defined(AES_128) + AESdecrypt(const unsigned char key[]) + { aes_decrypt_key128(key, cx); } + AES_RETURN key128(const unsigned char key[]) + { return aes_decrypt_key128(key, cx); } +#endif +#if defined(AES_192) + AES_RETURN key192(const unsigned char key[]) + { return aes_decrypt_key192(key, cx); } +#endif +#if defined(AES_256) + AES_RETURN key256(const unsigned char key[]) + { return aes_decrypt_key256(key, cx); } +#endif +#if defined(AES_VAR) + AES_RETURN key(const unsigned char key[], int key_len) + { return aes_decrypt_key(key, key_len, cx); } +#endif + AES_RETURN decrypt(const unsigned char in[], unsigned char out[]) const + { return aes_decrypt(in, out, cx); } +#ifndef AES_MODES + AES_RETURN ecb_decrypt(const unsigned char in[], unsigned char out[], int nb) const + { while(nb--) + { aes_decrypt(in, out, cx), in += AES_BLOCK_SIZE, out += AES_BLOCK_SIZE; } + } +#endif +#ifdef AES_MODES + + AES_RETURN ecb_decrypt(const unsigned char in[], unsigned char out[], int nb) const + { return aes_ecb_decrypt(in, out, nb, cx); } + + AES_RETURN cbc_decrypt(const unsigned char in[], unsigned char out[], int nb, + unsigned char iv[]) const + { return aes_cbc_decrypt(in, out, nb, iv, cx); } +#endif +}; + +#endif + +#endif diff --git a/src/java/KP2AKdbLibrary/jni/aes/aescrypt.c b/src/java/KP2AKdbLibrary/jni/aes/aescrypt.c new file mode 100644 index 00000000..f11e56c3 --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/aescrypt.c @@ -0,0 +1,301 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 20/12/2007 +*/ + +#include "aesopt.h" +#include "aestab.h" + +#if defined(__cplusplus) +extern "C" +{ +#endif + +#define si(y,x,k,c) (s(y,c) = word_in(x, c) ^ (k)[c]) +#define so(y,x,c) word_out(y, c, s(x,c)) + +#if defined(ARRAYS) +#define locals(y,x) x[4],y[4] +#else +#define locals(y,x) x##0,x##1,x##2,x##3,y##0,y##1,y##2,y##3 +#endif + +#define l_copy(y, x) s(y,0) = s(x,0); s(y,1) = s(x,1); \ + s(y,2) = s(x,2); s(y,3) = s(x,3); +#define state_in(y,x,k) si(y,x,k,0); si(y,x,k,1); si(y,x,k,2); si(y,x,k,3) +#define state_out(y,x) so(y,x,0); so(y,x,1); so(y,x,2); so(y,x,3) +#define round(rm,y,x,k) rm(y,x,k,0); rm(y,x,k,1); rm(y,x,k,2); rm(y,x,k,3) + +#if ( FUNCS_IN_C & ENCRYPTION_IN_C ) + +/* Visual C++ .Net v7.1 provides the fastest encryption code when using + Pentium optimiation with small code but this is poor for decryption + so we need to control this with the following VC++ pragmas +*/ + +#if defined( _MSC_VER ) && !defined( _WIN64 ) +#pragma optimize( "s", on ) +#endif + +/* Given the column (c) of the output state variable, the following + macros give the input state variables which are needed in its + computation for each row (r) of the state. All the alternative + macros give the same end values but expand into different ways + of calculating these values. In particular the complex macro + used for dynamically variable block sizes is designed to expand + to a compile time constant whenever possible but will expand to + conditional clauses on some branches (I am grateful to Frank + Yellin for this construction) +*/ + +#define fwd_var(x,r,c)\ + ( r == 0 ? ( c == 0 ? s(x,0) : c == 1 ? s(x,1) : c == 2 ? s(x,2) : s(x,3))\ + : r == 1 ? ( c == 0 ? s(x,1) : c == 1 ? s(x,2) : c == 2 ? s(x,3) : s(x,0))\ + : r == 2 ? ( c == 0 ? s(x,2) : c == 1 ? s(x,3) : c == 2 ? s(x,0) : s(x,1))\ + : ( c == 0 ? s(x,3) : c == 1 ? s(x,0) : c == 2 ? s(x,1) : s(x,2))) + +#if defined(FT4_SET) +#undef dec_fmvars +#define fwd_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ four_tables(x,t_use(f,n),fwd_var,rf1,c)) +#elif defined(FT1_SET) +#undef dec_fmvars +#define fwd_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ one_table(x,upr,t_use(f,n),fwd_var,rf1,c)) +#else +#define fwd_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ fwd_mcol(no_table(x,t_use(s,box),fwd_var,rf1,c))) +#endif + +#if defined(FL4_SET) +#define fwd_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ four_tables(x,t_use(f,l),fwd_var,rf1,c)) +#elif defined(FL1_SET) +#define fwd_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ one_table(x,ups,t_use(f,l),fwd_var,rf1,c)) +#else +#define fwd_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ no_table(x,t_use(s,box),fwd_var,rf1,c)) +#endif + +AES_RETURN aes_encrypt(const unsigned char *in, unsigned char *out, const aes_encrypt_ctx cx[1]) +{ uint_32t locals(b0, b1); + const uint_32t *kp; +#if defined( dec_fmvars ) + dec_fmvars; /* declare variables for fwd_mcol() if needed */ +#endif + + if( cx->inf.b[0] != 10 * 16 && cx->inf.b[0] != 12 * 16 && cx->inf.b[0] != 14 * 16 ) + return EXIT_FAILURE; + + kp = cx->ks; + state_in(b0, in, kp); + +#if (ENC_UNROLL == FULL) + + switch(cx->inf.b[0]) + { + case 14 * 16: + round(fwd_rnd, b1, b0, kp + 1 * N_COLS); + round(fwd_rnd, b0, b1, kp + 2 * N_COLS); + kp += 2 * N_COLS; + case 12 * 16: + round(fwd_rnd, b1, b0, kp + 1 * N_COLS); + round(fwd_rnd, b0, b1, kp + 2 * N_COLS); + kp += 2 * N_COLS; + case 10 * 16: + round(fwd_rnd, b1, b0, kp + 1 * N_COLS); + round(fwd_rnd, b0, b1, kp + 2 * N_COLS); + round(fwd_rnd, b1, b0, kp + 3 * N_COLS); + round(fwd_rnd, b0, b1, kp + 4 * N_COLS); + round(fwd_rnd, b1, b0, kp + 5 * N_COLS); + round(fwd_rnd, b0, b1, kp + 6 * N_COLS); + round(fwd_rnd, b1, b0, kp + 7 * N_COLS); + round(fwd_rnd, b0, b1, kp + 8 * N_COLS); + round(fwd_rnd, b1, b0, kp + 9 * N_COLS); + round(fwd_lrnd, b0, b1, kp +10 * N_COLS); + } + +#else + +#if (ENC_UNROLL == PARTIAL) + { uint_32t rnd; + for(rnd = 0; rnd < (cx->inf.b[0] >> 5) - 1; ++rnd) + { + kp += N_COLS; + round(fwd_rnd, b1, b0, kp); + kp += N_COLS; + round(fwd_rnd, b0, b1, kp); + } + kp += N_COLS; + round(fwd_rnd, b1, b0, kp); +#else + { uint_32t rnd; + for(rnd = 0; rnd < (cx->inf.b[0] >> 4) - 1; ++rnd) + { + kp += N_COLS; + round(fwd_rnd, b1, b0, kp); + l_copy(b0, b1); + } +#endif + kp += N_COLS; + round(fwd_lrnd, b0, b1, kp); + } +#endif + + state_out(out, b0); + return EXIT_SUCCESS; +} + +#endif + +#if ( FUNCS_IN_C & DECRYPTION_IN_C) + +/* Visual C++ .Net v7.1 provides the fastest encryption code when using + Pentium optimiation with small code but this is poor for decryption + so we need to control this with the following VC++ pragmas +*/ + +#if defined( _MSC_VER ) && !defined( _WIN64 ) +#pragma optimize( "t", on ) +#endif + +/* Given the column (c) of the output state variable, the following + macros give the input state variables which are needed in its + computation for each row (r) of the state. All the alternative + macros give the same end values but expand into different ways + of calculating these values. In particular the complex macro + used for dynamically variable block sizes is designed to expand + to a compile time constant whenever possible but will expand to + conditional clauses on some branches (I am grateful to Frank + Yellin for this construction) +*/ + +#define inv_var(x,r,c)\ + ( r == 0 ? ( c == 0 ? s(x,0) : c == 1 ? s(x,1) : c == 2 ? s(x,2) : s(x,3))\ + : r == 1 ? ( c == 0 ? s(x,3) : c == 1 ? s(x,0) : c == 2 ? s(x,1) : s(x,2))\ + : r == 2 ? ( c == 0 ? s(x,2) : c == 1 ? s(x,3) : c == 2 ? s(x,0) : s(x,1))\ + : ( c == 0 ? s(x,1) : c == 1 ? s(x,2) : c == 2 ? s(x,3) : s(x,0))) + +#if defined(IT4_SET) +#undef dec_imvars +#define inv_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ four_tables(x,t_use(i,n),inv_var,rf1,c)) +#elif defined(IT1_SET) +#undef dec_imvars +#define inv_rnd(y,x,k,c) (s(y,c) = (k)[c] ^ one_table(x,upr,t_use(i,n),inv_var,rf1,c)) +#else +#define inv_rnd(y,x,k,c) (s(y,c) = inv_mcol((k)[c] ^ no_table(x,t_use(i,box),inv_var,rf1,c))) +#endif + +#if defined(IL4_SET) +#define inv_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ four_tables(x,t_use(i,l),inv_var,rf1,c)) +#elif defined(IL1_SET) +#define inv_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ one_table(x,ups,t_use(i,l),inv_var,rf1,c)) +#else +#define inv_lrnd(y,x,k,c) (s(y,c) = (k)[c] ^ no_table(x,t_use(i,box),inv_var,rf1,c)) +#endif + +/* This code can work with the decryption key schedule in the */ +/* order that is used for encrytpion (where the 1st decryption */ +/* round key is at the high end ot the schedule) or with a key */ +/* schedule that has been reversed to put the 1st decryption */ +/* round key at the low end of the schedule in memory (when */ +/* AES_REV_DKS is defined) */ + +#ifdef AES_REV_DKS +#define key_ofs 0 +#define rnd_key(n) (kp + n * N_COLS) +#else +#define key_ofs 1 +#define rnd_key(n) (kp - n * N_COLS) +#endif + +AES_RETURN aes_decrypt(const unsigned char *in, unsigned char *out, const aes_decrypt_ctx cx[1]) +{ uint_32t locals(b0, b1); +#if defined( dec_imvars ) + dec_imvars; /* declare variables for inv_mcol() if needed */ +#endif + const uint_32t *kp; + + if( cx->inf.b[0] != 10 * 16 && cx->inf.b[0] != 12 * 16 && cx->inf.b[0] != 14 * 16 ) + return EXIT_FAILURE; + + kp = cx->ks + (key_ofs ? (cx->inf.b[0] >> 2) : 0); + state_in(b0, in, kp); + +#if (DEC_UNROLL == FULL) + + kp = cx->ks + (key_ofs ? 0 : (cx->inf.b[0] >> 2)); + switch(cx->inf.b[0]) + { + case 14 * 16: + round(inv_rnd, b1, b0, rnd_key(-13)); + round(inv_rnd, b0, b1, rnd_key(-12)); + case 12 * 16: + round(inv_rnd, b1, b0, rnd_key(-11)); + round(inv_rnd, b0, b1, rnd_key(-10)); + case 10 * 16: + round(inv_rnd, b1, b0, rnd_key(-9)); + round(inv_rnd, b0, b1, rnd_key(-8)); + round(inv_rnd, b1, b0, rnd_key(-7)); + round(inv_rnd, b0, b1, rnd_key(-6)); + round(inv_rnd, b1, b0, rnd_key(-5)); + round(inv_rnd, b0, b1, rnd_key(-4)); + round(inv_rnd, b1, b0, rnd_key(-3)); + round(inv_rnd, b0, b1, rnd_key(-2)); + round(inv_rnd, b1, b0, rnd_key(-1)); + round(inv_lrnd, b0, b1, rnd_key( 0)); + } + +#else + +#if (DEC_UNROLL == PARTIAL) + { uint_32t rnd; + for(rnd = 0; rnd < (cx->inf.b[0] >> 5) - 1; ++rnd) + { + kp = rnd_key(1); + round(inv_rnd, b1, b0, kp); + kp = rnd_key(1); + round(inv_rnd, b0, b1, kp); + } + kp = rnd_key(1); + round(inv_rnd, b1, b0, kp); +#else + { uint_32t rnd; + for(rnd = 0; rnd < (cx->inf.b[0] >> 4) - 1; ++rnd) + { + kp = rnd_key(1); + round(inv_rnd, b1, b0, kp); + l_copy(b0, b1); + } +#endif + kp = rnd_key(1); + round(inv_lrnd, b0, b1, kp); + } +#endif + + state_out(out, b0); + return EXIT_SUCCESS; +} + +#endif + +#if defined(__cplusplus) +} +#endif diff --git a/src/java/KP2AKdbLibrary/jni/aes/aeskey.c b/src/java/KP2AKdbLibrary/jni/aes/aeskey.c new file mode 100644 index 00000000..fb122b24 --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/aeskey.c @@ -0,0 +1,555 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 20/12/2007 +*/ + +#include "aesopt.h" +#include "aestab.h" + +#ifdef USE_VIA_ACE_IF_PRESENT +# include "aes_via_ace.h" +#endif + +#if defined(__cplusplus) +extern "C" +{ +#endif + +/* Initialise the key schedule from the user supplied key. The key + length can be specified in bytes, with legal values of 16, 24 + and 32, or in bits, with legal values of 128, 192 and 256. These + values correspond with Nk values of 4, 6 and 8 respectively. + + The following macros implement a single cycle in the key + schedule generation process. The number of cycles needed + for each cx->n_col and nk value is: + + nk = 4 5 6 7 8 + ------------------------------ + cx->n_col = 4 10 9 8 7 7 + cx->n_col = 5 14 11 10 9 9 + cx->n_col = 6 19 15 12 11 11 + cx->n_col = 7 21 19 16 13 14 + cx->n_col = 8 29 23 19 17 14 +*/ + +#if defined( REDUCE_CODE_SIZE ) +# define ls_box ls_sub + uint_32t ls_sub(const uint_32t t, const uint_32t n); +# define inv_mcol im_sub + uint_32t im_sub(const uint_32t x); +# ifdef ENC_KS_UNROLL +# undef ENC_KS_UNROLL +# endif +# ifdef DEC_KS_UNROLL +# undef DEC_KS_UNROLL +# endif +#endif + +#if (FUNCS_IN_C & ENC_KEYING_IN_C) + +#if defined(AES_128) || defined( AES_VAR ) + +#define ke4(k,i) \ +{ k[4*(i)+4] = ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; \ + k[4*(i)+5] = ss[1] ^= ss[0]; \ + k[4*(i)+6] = ss[2] ^= ss[1]; \ + k[4*(i)+7] = ss[3] ^= ss[2]; \ +} + +AES_RETURN aes_encrypt_key128(const unsigned char *key, aes_encrypt_ctx cx[1]) +{ uint_32t ss[4]; + + cx->ks[0] = ss[0] = word_in(key, 0); + cx->ks[1] = ss[1] = word_in(key, 1); + cx->ks[2] = ss[2] = word_in(key, 2); + cx->ks[3] = ss[3] = word_in(key, 3); + +#ifdef ENC_KS_UNROLL + ke4(cx->ks, 0); ke4(cx->ks, 1); + ke4(cx->ks, 2); ke4(cx->ks, 3); + ke4(cx->ks, 4); ke4(cx->ks, 5); + ke4(cx->ks, 6); ke4(cx->ks, 7); + ke4(cx->ks, 8); +#else + { uint_32t i; + for(i = 0; i < 9; ++i) + ke4(cx->ks, i); + } +#endif + ke4(cx->ks, 9); + cx->inf.l = 0; + cx->inf.b[0] = 10 * 16; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) + cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#if defined(AES_192) || defined( AES_VAR ) + +#define kef6(k,i) \ +{ k[6*(i)+ 6] = ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; \ + k[6*(i)+ 7] = ss[1] ^= ss[0]; \ + k[6*(i)+ 8] = ss[2] ^= ss[1]; \ + k[6*(i)+ 9] = ss[3] ^= ss[2]; \ +} + +#define ke6(k,i) \ +{ kef6(k,i); \ + k[6*(i)+10] = ss[4] ^= ss[3]; \ + k[6*(i)+11] = ss[5] ^= ss[4]; \ +} + +AES_RETURN aes_encrypt_key192(const unsigned char *key, aes_encrypt_ctx cx[1]) +{ uint_32t ss[6]; + + cx->ks[0] = ss[0] = word_in(key, 0); + cx->ks[1] = ss[1] = word_in(key, 1); + cx->ks[2] = ss[2] = word_in(key, 2); + cx->ks[3] = ss[3] = word_in(key, 3); + cx->ks[4] = ss[4] = word_in(key, 4); + cx->ks[5] = ss[5] = word_in(key, 5); + +#ifdef ENC_KS_UNROLL + ke6(cx->ks, 0); ke6(cx->ks, 1); + ke6(cx->ks, 2); ke6(cx->ks, 3); + ke6(cx->ks, 4); ke6(cx->ks, 5); + ke6(cx->ks, 6); +#else + { uint_32t i; + for(i = 0; i < 7; ++i) + ke6(cx->ks, i); + } +#endif + kef6(cx->ks, 7); + cx->inf.l = 0; + cx->inf.b[0] = 12 * 16; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) + cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#if defined(AES_256) || defined( AES_VAR ) + +#define kef8(k,i) \ +{ k[8*(i)+ 8] = ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; \ + k[8*(i)+ 9] = ss[1] ^= ss[0]; \ + k[8*(i)+10] = ss[2] ^= ss[1]; \ + k[8*(i)+11] = ss[3] ^= ss[2]; \ +} + +#define ke8(k,i) \ +{ kef8(k,i); \ + k[8*(i)+12] = ss[4] ^= ls_box(ss[3],0); \ + k[8*(i)+13] = ss[5] ^= ss[4]; \ + k[8*(i)+14] = ss[6] ^= ss[5]; \ + k[8*(i)+15] = ss[7] ^= ss[6]; \ +} + +AES_RETURN aes_encrypt_key256(const unsigned char *key, aes_encrypt_ctx cx[1]) +{ uint_32t ss[8]; + + cx->ks[0] = ss[0] = word_in(key, 0); + cx->ks[1] = ss[1] = word_in(key, 1); + cx->ks[2] = ss[2] = word_in(key, 2); + cx->ks[3] = ss[3] = word_in(key, 3); + cx->ks[4] = ss[4] = word_in(key, 4); + cx->ks[5] = ss[5] = word_in(key, 5); + cx->ks[6] = ss[6] = word_in(key, 6); + cx->ks[7] = ss[7] = word_in(key, 7); + +#ifdef ENC_KS_UNROLL + ke8(cx->ks, 0); ke8(cx->ks, 1); + ke8(cx->ks, 2); ke8(cx->ks, 3); + ke8(cx->ks, 4); ke8(cx->ks, 5); +#else + { uint_32t i; + for(i = 0; i < 6; ++i) + ke8(cx->ks, i); + } +#endif + kef8(cx->ks, 6); + cx->inf.l = 0; + cx->inf.b[0] = 14 * 16; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) + cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#if defined( AES_VAR ) + +AES_RETURN aes_encrypt_key(const unsigned char *key, int key_len, aes_encrypt_ctx cx[1]) +{ + switch(key_len) + { + case 16: case 128: return aes_encrypt_key128(key, cx); + case 24: case 192: return aes_encrypt_key192(key, cx); + case 32: case 256: return aes_encrypt_key256(key, cx); + default: return EXIT_FAILURE; + } +} + +#endif + +#endif + +#if (FUNCS_IN_C & DEC_KEYING_IN_C) + +/* this is used to store the decryption round keys */ +/* in forward or reverse order */ + +#ifdef AES_REV_DKS +#define v(n,i) ((n) - (i) + 2 * ((i) & 3)) +#else +#define v(n,i) (i) +#endif + +#if DEC_ROUND == NO_TABLES +#define ff(x) (x) +#else +#define ff(x) inv_mcol(x) +#if defined( dec_imvars ) +#define d_vars dec_imvars +#endif +#endif + +#if defined(AES_128) || defined( AES_VAR ) + +#define k4e(k,i) \ +{ k[v(40,(4*(i))+4)] = ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; \ + k[v(40,(4*(i))+5)] = ss[1] ^= ss[0]; \ + k[v(40,(4*(i))+6)] = ss[2] ^= ss[1]; \ + k[v(40,(4*(i))+7)] = ss[3] ^= ss[2]; \ +} + +#if 1 + +#define kdf4(k,i) \ +{ ss[0] = ss[0] ^ ss[2] ^ ss[1] ^ ss[3]; \ + ss[1] = ss[1] ^ ss[3]; \ + ss[2] = ss[2] ^ ss[3]; \ + ss[4] = ls_box(ss[(i+3) % 4], 3) ^ t_use(r,c)[i]; \ + ss[i % 4] ^= ss[4]; \ + ss[4] ^= k[v(40,(4*(i)))]; k[v(40,(4*(i))+4)] = ff(ss[4]); \ + ss[4] ^= k[v(40,(4*(i))+1)]; k[v(40,(4*(i))+5)] = ff(ss[4]); \ + ss[4] ^= k[v(40,(4*(i))+2)]; k[v(40,(4*(i))+6)] = ff(ss[4]); \ + ss[4] ^= k[v(40,(4*(i))+3)]; k[v(40,(4*(i))+7)] = ff(ss[4]); \ +} + +#define kd4(k,i) \ +{ ss[4] = ls_box(ss[(i+3) % 4], 3) ^ t_use(r,c)[i]; \ + ss[i % 4] ^= ss[4]; ss[4] = ff(ss[4]); \ + k[v(40,(4*(i))+4)] = ss[4] ^= k[v(40,(4*(i)))]; \ + k[v(40,(4*(i))+5)] = ss[4] ^= k[v(40,(4*(i))+1)]; \ + k[v(40,(4*(i))+6)] = ss[4] ^= k[v(40,(4*(i))+2)]; \ + k[v(40,(4*(i))+7)] = ss[4] ^= k[v(40,(4*(i))+3)]; \ +} + +#define kdl4(k,i) \ +{ ss[4] = ls_box(ss[(i+3) % 4], 3) ^ t_use(r,c)[i]; ss[i % 4] ^= ss[4]; \ + k[v(40,(4*(i))+4)] = (ss[0] ^= ss[1]) ^ ss[2] ^ ss[3]; \ + k[v(40,(4*(i))+5)] = ss[1] ^ ss[3]; \ + k[v(40,(4*(i))+6)] = ss[0]; \ + k[v(40,(4*(i))+7)] = ss[1]; \ +} + +#else + +#define kdf4(k,i) \ +{ ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; k[v(40,(4*(i))+ 4)] = ff(ss[0]); \ + ss[1] ^= ss[0]; k[v(40,(4*(i))+ 5)] = ff(ss[1]); \ + ss[2] ^= ss[1]; k[v(40,(4*(i))+ 6)] = ff(ss[2]); \ + ss[3] ^= ss[2]; k[v(40,(4*(i))+ 7)] = ff(ss[3]); \ +} + +#define kd4(k,i) \ +{ ss[4] = ls_box(ss[3],3) ^ t_use(r,c)[i]; \ + ss[0] ^= ss[4]; ss[4] = ff(ss[4]); k[v(40,(4*(i))+ 4)] = ss[4] ^= k[v(40,(4*(i)))]; \ + ss[1] ^= ss[0]; k[v(40,(4*(i))+ 5)] = ss[4] ^= k[v(40,(4*(i))+ 1)]; \ + ss[2] ^= ss[1]; k[v(40,(4*(i))+ 6)] = ss[4] ^= k[v(40,(4*(i))+ 2)]; \ + ss[3] ^= ss[2]; k[v(40,(4*(i))+ 7)] = ss[4] ^= k[v(40,(4*(i))+ 3)]; \ +} + +#define kdl4(k,i) \ +{ ss[0] ^= ls_box(ss[3],3) ^ t_use(r,c)[i]; k[v(40,(4*(i))+ 4)] = ss[0]; \ + ss[1] ^= ss[0]; k[v(40,(4*(i))+ 5)] = ss[1]; \ + ss[2] ^= ss[1]; k[v(40,(4*(i))+ 6)] = ss[2]; \ + ss[3] ^= ss[2]; k[v(40,(4*(i))+ 7)] = ss[3]; \ +} + +#endif + +AES_RETURN aes_decrypt_key128(const unsigned char *key, aes_decrypt_ctx cx[1]) +{ uint_32t ss[5]; +#if defined( d_vars ) + d_vars; +#endif + cx->ks[v(40,(0))] = ss[0] = word_in(key, 0); + cx->ks[v(40,(1))] = ss[1] = word_in(key, 1); + cx->ks[v(40,(2))] = ss[2] = word_in(key, 2); + cx->ks[v(40,(3))] = ss[3] = word_in(key, 3); + +#ifdef DEC_KS_UNROLL + kdf4(cx->ks, 0); kd4(cx->ks, 1); + kd4(cx->ks, 2); kd4(cx->ks, 3); + kd4(cx->ks, 4); kd4(cx->ks, 5); + kd4(cx->ks, 6); kd4(cx->ks, 7); + kd4(cx->ks, 8); kdl4(cx->ks, 9); +#else + { uint_32t i; + for(i = 0; i < 10; ++i) + k4e(cx->ks, i); +#if !(DEC_ROUND == NO_TABLES) + for(i = N_COLS; i < 10 * N_COLS; ++i) + cx->ks[i] = inv_mcol(cx->ks[i]); +#endif + } +#endif + cx->inf.l = 0; + cx->inf.b[0] = 10 * 16; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) + cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#if defined(AES_192) || defined( AES_VAR ) + +#define k6ef(k,i) \ +{ k[v(48,(6*(i))+ 6)] = ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; \ + k[v(48,(6*(i))+ 7)] = ss[1] ^= ss[0]; \ + k[v(48,(6*(i))+ 8)] = ss[2] ^= ss[1]; \ + k[v(48,(6*(i))+ 9)] = ss[3] ^= ss[2]; \ +} + +#define k6e(k,i) \ +{ k6ef(k,i); \ + k[v(48,(6*(i))+10)] = ss[4] ^= ss[3]; \ + k[v(48,(6*(i))+11)] = ss[5] ^= ss[4]; \ +} + +#define kdf6(k,i) \ +{ ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; k[v(48,(6*(i))+ 6)] = ff(ss[0]); \ + ss[1] ^= ss[0]; k[v(48,(6*(i))+ 7)] = ff(ss[1]); \ + ss[2] ^= ss[1]; k[v(48,(6*(i))+ 8)] = ff(ss[2]); \ + ss[3] ^= ss[2]; k[v(48,(6*(i))+ 9)] = ff(ss[3]); \ + ss[4] ^= ss[3]; k[v(48,(6*(i))+10)] = ff(ss[4]); \ + ss[5] ^= ss[4]; k[v(48,(6*(i))+11)] = ff(ss[5]); \ +} + +#define kd6(k,i) \ +{ ss[6] = ls_box(ss[5],3) ^ t_use(r,c)[i]; \ + ss[0] ^= ss[6]; ss[6] = ff(ss[6]); k[v(48,(6*(i))+ 6)] = ss[6] ^= k[v(48,(6*(i)))]; \ + ss[1] ^= ss[0]; k[v(48,(6*(i))+ 7)] = ss[6] ^= k[v(48,(6*(i))+ 1)]; \ + ss[2] ^= ss[1]; k[v(48,(6*(i))+ 8)] = ss[6] ^= k[v(48,(6*(i))+ 2)]; \ + ss[3] ^= ss[2]; k[v(48,(6*(i))+ 9)] = ss[6] ^= k[v(48,(6*(i))+ 3)]; \ + ss[4] ^= ss[3]; k[v(48,(6*(i))+10)] = ss[6] ^= k[v(48,(6*(i))+ 4)]; \ + ss[5] ^= ss[4]; k[v(48,(6*(i))+11)] = ss[6] ^= k[v(48,(6*(i))+ 5)]; \ +} + +#define kdl6(k,i) \ +{ ss[0] ^= ls_box(ss[5],3) ^ t_use(r,c)[i]; k[v(48,(6*(i))+ 6)] = ss[0]; \ + ss[1] ^= ss[0]; k[v(48,(6*(i))+ 7)] = ss[1]; \ + ss[2] ^= ss[1]; k[v(48,(6*(i))+ 8)] = ss[2]; \ + ss[3] ^= ss[2]; k[v(48,(6*(i))+ 9)] = ss[3]; \ +} + +AES_RETURN aes_decrypt_key192(const unsigned char *key, aes_decrypt_ctx cx[1]) +{ uint_32t ss[7]; +#if defined( d_vars ) + d_vars; +#endif + cx->ks[v(48,(0))] = ss[0] = word_in(key, 0); + cx->ks[v(48,(1))] = ss[1] = word_in(key, 1); + cx->ks[v(48,(2))] = ss[2] = word_in(key, 2); + cx->ks[v(48,(3))] = ss[3] = word_in(key, 3); + +#ifdef DEC_KS_UNROLL + cx->ks[v(48,(4))] = ff(ss[4] = word_in(key, 4)); + cx->ks[v(48,(5))] = ff(ss[5] = word_in(key, 5)); + kdf6(cx->ks, 0); kd6(cx->ks, 1); + kd6(cx->ks, 2); kd6(cx->ks, 3); + kd6(cx->ks, 4); kd6(cx->ks, 5); + kd6(cx->ks, 6); kdl6(cx->ks, 7); +#else + cx->ks[v(48,(4))] = ss[4] = word_in(key, 4); + cx->ks[v(48,(5))] = ss[5] = word_in(key, 5); + { uint_32t i; + + for(i = 0; i < 7; ++i) + k6e(cx->ks, i); + k6ef(cx->ks, 7); +#if !(DEC_ROUND == NO_TABLES) + for(i = N_COLS; i < 12 * N_COLS; ++i) + cx->ks[i] = inv_mcol(cx->ks[i]); +#endif + } +#endif + cx->inf.l = 0; + cx->inf.b[0] = 12 * 16; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) + cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#if defined(AES_256) || defined( AES_VAR ) + +#define k8ef(k,i) \ +{ k[v(56,(8*(i))+ 8)] = ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; \ + k[v(56,(8*(i))+ 9)] = ss[1] ^= ss[0]; \ + k[v(56,(8*(i))+10)] = ss[2] ^= ss[1]; \ + k[v(56,(8*(i))+11)] = ss[3] ^= ss[2]; \ +} + +#define k8e(k,i) \ +{ k8ef(k,i); \ + k[v(56,(8*(i))+12)] = ss[4] ^= ls_box(ss[3],0); \ + k[v(56,(8*(i))+13)] = ss[5] ^= ss[4]; \ + k[v(56,(8*(i))+14)] = ss[6] ^= ss[5]; \ + k[v(56,(8*(i))+15)] = ss[7] ^= ss[6]; \ +} + +#define kdf8(k,i) \ +{ ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; k[v(56,(8*(i))+ 8)] = ff(ss[0]); \ + ss[1] ^= ss[0]; k[v(56,(8*(i))+ 9)] = ff(ss[1]); \ + ss[2] ^= ss[1]; k[v(56,(8*(i))+10)] = ff(ss[2]); \ + ss[3] ^= ss[2]; k[v(56,(8*(i))+11)] = ff(ss[3]); \ + ss[4] ^= ls_box(ss[3],0); k[v(56,(8*(i))+12)] = ff(ss[4]); \ + ss[5] ^= ss[4]; k[v(56,(8*(i))+13)] = ff(ss[5]); \ + ss[6] ^= ss[5]; k[v(56,(8*(i))+14)] = ff(ss[6]); \ + ss[7] ^= ss[6]; k[v(56,(8*(i))+15)] = ff(ss[7]); \ +} + +#define kd8(k,i) \ +{ ss[8] = ls_box(ss[7],3) ^ t_use(r,c)[i]; \ + ss[0] ^= ss[8]; ss[8] = ff(ss[8]); k[v(56,(8*(i))+ 8)] = ss[8] ^= k[v(56,(8*(i)))]; \ + ss[1] ^= ss[0]; k[v(56,(8*(i))+ 9)] = ss[8] ^= k[v(56,(8*(i))+ 1)]; \ + ss[2] ^= ss[1]; k[v(56,(8*(i))+10)] = ss[8] ^= k[v(56,(8*(i))+ 2)]; \ + ss[3] ^= ss[2]; k[v(56,(8*(i))+11)] = ss[8] ^= k[v(56,(8*(i))+ 3)]; \ + ss[8] = ls_box(ss[3],0); \ + ss[4] ^= ss[8]; ss[8] = ff(ss[8]); k[v(56,(8*(i))+12)] = ss[8] ^= k[v(56,(8*(i))+ 4)]; \ + ss[5] ^= ss[4]; k[v(56,(8*(i))+13)] = ss[8] ^= k[v(56,(8*(i))+ 5)]; \ + ss[6] ^= ss[5]; k[v(56,(8*(i))+14)] = ss[8] ^= k[v(56,(8*(i))+ 6)]; \ + ss[7] ^= ss[6]; k[v(56,(8*(i))+15)] = ss[8] ^= k[v(56,(8*(i))+ 7)]; \ +} + +#define kdl8(k,i) \ +{ ss[0] ^= ls_box(ss[7],3) ^ t_use(r,c)[i]; k[v(56,(8*(i))+ 8)] = ss[0]; \ + ss[1] ^= ss[0]; k[v(56,(8*(i))+ 9)] = ss[1]; \ + ss[2] ^= ss[1]; k[v(56,(8*(i))+10)] = ss[2]; \ + ss[3] ^= ss[2]; k[v(56,(8*(i))+11)] = ss[3]; \ +} + +AES_RETURN aes_decrypt_key256(const unsigned char *key, aes_decrypt_ctx cx[1]) +{ uint_32t ss[9]; +#if defined( d_vars ) + d_vars; +#endif + cx->ks[v(56,(0))] = ss[0] = word_in(key, 0); + cx->ks[v(56,(1))] = ss[1] = word_in(key, 1); + cx->ks[v(56,(2))] = ss[2] = word_in(key, 2); + cx->ks[v(56,(3))] = ss[3] = word_in(key, 3); + +#ifdef DEC_KS_UNROLL + cx->ks[v(56,(4))] = ff(ss[4] = word_in(key, 4)); + cx->ks[v(56,(5))] = ff(ss[5] = word_in(key, 5)); + cx->ks[v(56,(6))] = ff(ss[6] = word_in(key, 6)); + cx->ks[v(56,(7))] = ff(ss[7] = word_in(key, 7)); + kdf8(cx->ks, 0); kd8(cx->ks, 1); + kd8(cx->ks, 2); kd8(cx->ks, 3); + kd8(cx->ks, 4); kd8(cx->ks, 5); + kdl8(cx->ks, 6); +#else + cx->ks[v(56,(4))] = ss[4] = word_in(key, 4); + cx->ks[v(56,(5))] = ss[5] = word_in(key, 5); + cx->ks[v(56,(6))] = ss[6] = word_in(key, 6); + cx->ks[v(56,(7))] = ss[7] = word_in(key, 7); + { uint_32t i; + + for(i = 0; i < 6; ++i) + k8e(cx->ks, i); + k8ef(cx->ks, 6); +#if !(DEC_ROUND == NO_TABLES) + for(i = N_COLS; i < 14 * N_COLS; ++i) + cx->ks[i] = inv_mcol(cx->ks[i]); +#endif + } +#endif + cx->inf.l = 0; + cx->inf.b[0] = 14 * 16; + +#ifdef USE_VIA_ACE_IF_PRESENT + if(VIA_ACE_AVAILABLE) + cx->inf.b[1] = 0xff; +#endif + return EXIT_SUCCESS; +} + +#endif + +#if defined( AES_VAR ) + +AES_RETURN aes_decrypt_key(const unsigned char *key, int key_len, aes_decrypt_ctx cx[1]) +{ + switch(key_len) + { + case 16: case 128: return aes_decrypt_key128(key, cx); + case 24: case 192: return aes_decrypt_key192(key, cx); + case 32: case 256: return aes_decrypt_key256(key, cx); + default: return EXIT_FAILURE; + } +} + +#endif + +#endif + +#if defined(__cplusplus) +} +#endif diff --git a/src/java/KP2AKdbLibrary/jni/aes/aesopt.h b/src/java/KP2AKdbLibrary/jni/aes/aesopt.h new file mode 100644 index 00000000..3c77b87f --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/aesopt.h @@ -0,0 +1,747 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 20/12/2007 + + This file contains the compilation options for AES (Rijndael) and code + that is common across encryption, key scheduling and table generation. + + OPERATION + + These source code files implement the AES algorithm Rijndael designed by + Joan Daemen and Vincent Rijmen. This version is designed for the standard + block size of 16 bytes and for key sizes of 128, 192 and 256 bits (16, 24 + and 32 bytes). + + This version is designed for flexibility and speed using operations on + 32-bit words rather than operations on bytes. It can be compiled with + either big or little endian internal byte order but is faster when the + native byte order for the processor is used. + + THE CIPHER INTERFACE + + The cipher interface is implemented as an array of bytes in which lower + AES bit sequence indexes map to higher numeric significance within bytes. + + uint_8t (an unsigned 8-bit type) + uint_32t (an unsigned 32-bit type) + struct aes_encrypt_ctx (structure for the cipher encryption context) + struct aes_decrypt_ctx (structure for the cipher decryption context) + AES_RETURN the function return type + + C subroutine calls: + + AES_RETURN aes_encrypt_key128(const unsigned char *key, aes_encrypt_ctx cx[1]); + AES_RETURN aes_encrypt_key192(const unsigned char *key, aes_encrypt_ctx cx[1]); + AES_RETURN aes_encrypt_key256(const unsigned char *key, aes_encrypt_ctx cx[1]); + AES_RETURN aes_encrypt(const unsigned char *in, unsigned char *out, + const aes_encrypt_ctx cx[1]); + + AES_RETURN aes_decrypt_key128(const unsigned char *key, aes_decrypt_ctx cx[1]); + AES_RETURN aes_decrypt_key192(const unsigned char *key, aes_decrypt_ctx cx[1]); + AES_RETURN aes_decrypt_key256(const unsigned char *key, aes_decrypt_ctx cx[1]); + AES_RETURN aes_decrypt(const unsigned char *in, unsigned char *out, + const aes_decrypt_ctx cx[1]); + + IMPORTANT NOTE: If you are using this C interface with dynamic tables make sure that + you call aes_init() before AES is used so that the tables are initialised. + + C++ aes class subroutines: + + Class AESencrypt for encryption + + Construtors: + AESencrypt(void) + AESencrypt(const unsigned char *key) - 128 bit key + Members: + AES_RETURN key128(const unsigned char *key) + AES_RETURN key192(const unsigned char *key) + AES_RETURN key256(const unsigned char *key) + AES_RETURN encrypt(const unsigned char *in, unsigned char *out) const + + Class AESdecrypt for encryption + Construtors: + AESdecrypt(void) + AESdecrypt(const unsigned char *key) - 128 bit key + Members: + AES_RETURN key128(const unsigned char *key) + AES_RETURN key192(const unsigned char *key) + AES_RETURN key256(const unsigned char *key) + AES_RETURN decrypt(const unsigned char *in, unsigned char *out) const +*/ + +#if !defined( _AESOPT_H ) +#define _AESOPT_H + +#if defined( __cplusplus ) +#include "aescpp.h" +#else +#include "aes.h" +#endif + +/* PLATFORM SPECIFIC INCLUDES */ + +#include "brg_endian.h" + +/* CONFIGURATION - THE USE OF DEFINES + + Later in this section there are a number of defines that control the + operation of the code. In each section, the purpose of each define is + explained so that the relevant form can be included or excluded by + setting either 1's or 0's respectively on the branches of the related + #if clauses. The following local defines should not be changed. +*/ + +#define ENCRYPTION_IN_C 1 +#define DECRYPTION_IN_C 2 +#define ENC_KEYING_IN_C 4 +#define DEC_KEYING_IN_C 8 + +#define NO_TABLES 0 +#define ONE_TABLE 1 +#define FOUR_TABLES 4 +#define NONE 0 +#define PARTIAL 1 +#define FULL 2 + +/* --- START OF USER CONFIGURED OPTIONS --- */ + +/* 1. BYTE ORDER WITHIN 32 BIT WORDS + + The fundamental data processing units in Rijndael are 8-bit bytes. The + input, output and key input are all enumerated arrays of bytes in which + bytes are numbered starting at zero and increasing to one less than the + number of bytes in the array in question. This enumeration is only used + for naming bytes and does not imply any adjacency or order relationship + from one byte to another. When these inputs and outputs are considered + as bit sequences, bits 8*n to 8*n+7 of the bit sequence are mapped to + byte[n] with bit 8n+i in the sequence mapped to bit 7-i within the byte. + In this implementation bits are numbered from 0 to 7 starting at the + numerically least significant end of each byte (bit n represents 2^n). + + However, Rijndael can be implemented more efficiently using 32-bit + words by packing bytes into words so that bytes 4*n to 4*n+3 are placed + into word[n]. While in principle these bytes can be assembled into words + in any positions, this implementation only supports the two formats in + which bytes in adjacent positions within words also have adjacent byte + numbers. This order is called big-endian if the lowest numbered bytes + in words have the highest numeric significance and little-endian if the + opposite applies. + + This code can work in either order irrespective of the order used by the + machine on which it runs. Normally the internal byte order will be set + to the order of the processor on which the code is to be run but this + define can be used to reverse this in special situations + + WARNING: Assembler code versions rely on PLATFORM_BYTE_ORDER being set. + This define will hence be redefined later (in section 4) if necessary +*/ + +#if 1 +# define ALGORITHM_BYTE_ORDER PLATFORM_BYTE_ORDER +#elif 0 +# define ALGORITHM_BYTE_ORDER IS_LITTLE_ENDIAN +#elif 0 +# define ALGORITHM_BYTE_ORDER IS_BIG_ENDIAN +#else +# error The algorithm byte order is not defined +#endif + +/* 2. VIA ACE SUPPORT */ + +#if defined( __GNUC__ ) && defined( __i386__ ) \ + || defined( _WIN32 ) && defined( _M_IX86 ) \ + && !(defined( _WIN64 ) || defined( _WIN32_WCE ) || defined( _MSC_VER ) && ( _MSC_VER <= 800 )) +# define VIA_ACE_POSSIBLE +#endif + +/* Define this option if support for the VIA ACE is required. This uses + inline assembler instructions and is only implemented for the Microsoft, + Intel and GCC compilers. If VIA ACE is known to be present, then defining + ASSUME_VIA_ACE_PRESENT will remove the ordinary encryption/decryption + code. If USE_VIA_ACE_IF_PRESENT is defined then VIA ACE will be used if + it is detected (both present and enabled) but the normal AES code will + also be present. + + When VIA ACE is to be used, all AES encryption contexts MUST be 16 byte + aligned; other input/output buffers do not need to be 16 byte aligned + but there are very large performance gains if this can be arranged. + VIA ACE also requires the decryption key schedule to be in reverse + order (which later checks below ensure). +*/ + +/* Disable VIA ACE cpu detection which crashes on x86 android devices */ +#if 0 && defined( VIA_ACE_POSSIBLE ) && !defined( USE_VIA_ACE_IF_PRESENT ) +# define USE_VIA_ACE_IF_PRESENT +#endif + +#if 0 && defined( VIA_ACE_POSSIBLE ) && !defined( ASSUME_VIA_ACE_PRESENT ) +# define ASSUME_VIA_ACE_PRESENT +# endif + +/* 3. ASSEMBLER SUPPORT + + This define (which can be on the command line) enables the use of the + assembler code routines for encryption, decryption and key scheduling + as follows: + + ASM_X86_V1C uses the assembler (aes_x86_v1.asm) with large tables for + encryption and decryption and but with key scheduling in C + ASM_X86_V2 uses assembler (aes_x86_v2.asm) with compressed tables for + encryption, decryption and key scheduling + ASM_X86_V2C uses assembler (aes_x86_v2.asm) with compressed tables for + encryption and decryption and but with key scheduling in C + ASM_AMD64_C uses assembler (aes_amd64.asm) with compressed tables for + encryption and decryption and but with key scheduling in C + + Change one 'if 0' below to 'if 1' to select the version or define + as a compilation option. +*/ + +#if 0 && !defined( ASM_X86_V1C ) +# define ASM_X86_V1C +#elif 0 && !defined( ASM_X86_V2 ) +# define ASM_X86_V2 +#elif 0 && !defined( ASM_X86_V2C ) +# define ASM_X86_V2C +#elif 0 && !defined( ASM_AMD64_C ) +# define ASM_AMD64_C +#endif + +#if (defined ( ASM_X86_V1C ) || defined( ASM_X86_V2 ) || defined( ASM_X86_V2C )) \ + && !defined( _M_IX86 ) || defined( ASM_AMD64_C ) && !defined( _M_X64 ) +# error Assembler code is only available for x86 and AMD64 systems +#endif + +/* 4. FAST INPUT/OUTPUT OPERATIONS. + + On some machines it is possible to improve speed by transferring the + bytes in the input and output arrays to and from the internal 32-bit + variables by addressing these arrays as if they are arrays of 32-bit + words. On some machines this will always be possible but there may + be a large performance penalty if the byte arrays are not aligned on + the normal word boundaries. On other machines this technique will + lead to memory access errors when such 32-bit word accesses are not + properly aligned. The option SAFE_IO avoids such problems but will + often be slower on those machines that support misaligned access + (especially so if care is taken to align the input and output byte + arrays on 32-bit word boundaries). If SAFE_IO is not defined it is + assumed that access to byte arrays as if they are arrays of 32-bit + words will not cause problems when such accesses are misaligned. +*/ +#if 1 && !defined( _MSC_VER ) +# define SAFE_IO +#endif + +/* 5. LOOP UNROLLING + + The code for encryption and decrytpion cycles through a number of rounds + that can be implemented either in a loop or by expanding the code into a + long sequence of instructions, the latter producing a larger program but + one that will often be much faster. The latter is called loop unrolling. + There are also potential speed advantages in expanding two iterations in + a loop with half the number of iterations, which is called partial loop + unrolling. The following options allow partial or full loop unrolling + to be set independently for encryption and decryption +*/ +#if 1 +# define ENC_UNROLL FULL +#elif 0 +# define ENC_UNROLL PARTIAL +#else +# define ENC_UNROLL NONE +#endif + +#if 1 +# define DEC_UNROLL FULL +#elif 0 +# define DEC_UNROLL PARTIAL +#else +# define DEC_UNROLL NONE +#endif + +#if 1 +# define ENC_KS_UNROLL +#endif + +#if 1 +# define DEC_KS_UNROLL +#endif + +/* 6. FAST FINITE FIELD OPERATIONS + + If this section is included, tables are used to provide faster finite + field arithmetic (this has no effect if FIXED_TABLES is defined). +*/ +#if 1 +# define FF_TABLES +#endif + +/* 7. INTERNAL STATE VARIABLE FORMAT + + The internal state of Rijndael is stored in a number of local 32-bit + word varaibles which can be defined either as an array or as individual + names variables. Include this section if you want to store these local + varaibles in arrays. Otherwise individual local variables will be used. +*/ +#if 1 +# define ARRAYS +#endif + +/* 8. FIXED OR DYNAMIC TABLES + + When this section is included the tables used by the code are compiled + statically into the binary file. Otherwise the subroutine aes_init() + must be called to compute them before the code is first used. +*/ +#if 1 && !(defined( _MSC_VER ) && ( _MSC_VER <= 800 )) +# define FIXED_TABLES +#endif + +/* 9. MASKING OR CASTING FROM LONGER VALUES TO BYTES + + In some systems it is better to mask longer values to extract bytes + rather than using a cast. This option allows this choice. +*/ +#if 0 +# define to_byte(x) ((uint_8t)(x)) +#else +# define to_byte(x) ((x) & 0xff) +#endif + +/* 10. TABLE ALIGNMENT + + On some sytsems speed will be improved by aligning the AES large lookup + tables on particular boundaries. This define should be set to a power of + two giving the desired alignment. It can be left undefined if alignment + is not needed. This option is specific to the Microsft VC++ compiler - + it seems to sometimes cause trouble for the VC++ version 6 compiler. +*/ + +#if 1 && defined( _MSC_VER ) && ( _MSC_VER >= 1300 ) +# define TABLE_ALIGN 32 +#endif + +/* 11. REDUCE CODE AND TABLE SIZE + + This replaces some expanded macros with function calls if AES_ASM_V2 or + AES_ASM_V2C are defined +*/ + +#if 1 && (defined( ASM_X86_V2 ) || defined( ASM_X86_V2C )) +# define REDUCE_CODE_SIZE +#endif + +/* 12. TABLE OPTIONS + + This cipher proceeds by repeating in a number of cycles known as 'rounds' + which are implemented by a round function which can optionally be speeded + up using tables. The basic tables are each 256 32-bit words, with either + one or four tables being required for each round function depending on + how much speed is required. The encryption and decryption round functions + are different and the last encryption and decrytpion round functions are + different again making four different round functions in all. + + This means that: + 1. Normal encryption and decryption rounds can each use either 0, 1 + or 4 tables and table spaces of 0, 1024 or 4096 bytes each. + 2. The last encryption and decryption rounds can also use either 0, 1 + or 4 tables and table spaces of 0, 1024 or 4096 bytes each. + + Include or exclude the appropriate definitions below to set the number + of tables used by this implementation. +*/ + +#if 1 /* set tables for the normal encryption round */ +# define ENC_ROUND FOUR_TABLES +#elif 0 +# define ENC_ROUND ONE_TABLE +#else +# define ENC_ROUND NO_TABLES +#endif + +#if 1 /* set tables for the last encryption round */ +# define LAST_ENC_ROUND FOUR_TABLES +#elif 0 +# define LAST_ENC_ROUND ONE_TABLE +#else +# define LAST_ENC_ROUND NO_TABLES +#endif + +#if 1 /* set tables for the normal decryption round */ +# define DEC_ROUND FOUR_TABLES +#elif 0 +# define DEC_ROUND ONE_TABLE +#else +# define DEC_ROUND NO_TABLES +#endif + +#if 1 /* set tables for the last decryption round */ +# define LAST_DEC_ROUND FOUR_TABLES +#elif 0 +# define LAST_DEC_ROUND ONE_TABLE +#else +# define LAST_DEC_ROUND NO_TABLES +#endif + +/* The decryption key schedule can be speeded up with tables in the same + way that the round functions can. Include or exclude the following + defines to set this requirement. +*/ +#if 1 +# define KEY_SCHED FOUR_TABLES +#elif 0 +# define KEY_SCHED ONE_TABLE +#else +# define KEY_SCHED NO_TABLES +#endif + +/* ---- END OF USER CONFIGURED OPTIONS ---- */ + +/* VIA ACE support is only available for VC++ and GCC */ + +#if !defined( _MSC_VER ) && !defined( __GNUC__ ) +# if defined( ASSUME_VIA_ACE_PRESENT ) +# undef ASSUME_VIA_ACE_PRESENT +# endif +# if defined( USE_VIA_ACE_IF_PRESENT ) +# undef USE_VIA_ACE_IF_PRESENT +# endif +#endif + +#if defined( ASSUME_VIA_ACE_PRESENT ) && !defined( USE_VIA_ACE_IF_PRESENT ) +# define USE_VIA_ACE_IF_PRESENT +#endif + +#if defined( USE_VIA_ACE_IF_PRESENT ) && !defined ( AES_REV_DKS ) +# define AES_REV_DKS +#endif + +/* Assembler support requires the use of platform byte order */ + +#if ( defined( ASM_X86_V1C ) || defined( ASM_X86_V2C ) || defined( ASM_AMD64_C ) ) \ + && (ALGORITHM_BYTE_ORDER != PLATFORM_BYTE_ORDER) +# undef ALGORITHM_BYTE_ORDER +# define ALGORITHM_BYTE_ORDER PLATFORM_BYTE_ORDER +#endif + +/* In this implementation the columns of the state array are each held in + 32-bit words. The state array can be held in various ways: in an array + of words, in a number of individual word variables or in a number of + processor registers. The following define maps a variable name x and + a column number c to the way the state array variable is to be held. + The first define below maps the state into an array x[c] whereas the + second form maps the state into a number of individual variables x0, + x1, etc. Another form could map individual state colums to machine + register names. +*/ + +#if defined( ARRAYS ) +# define s(x,c) x[c] +#else +# define s(x,c) x##c +#endif + +/* This implementation provides subroutines for encryption, decryption + and for setting the three key lengths (separately) for encryption + and decryption. Since not all functions are needed, masks are set + up here to determine which will be implemented in C +*/ + +#if !defined( AES_ENCRYPT ) +# define EFUNCS_IN_C 0 +#elif defined( ASSUME_VIA_ACE_PRESENT ) || defined( ASM_X86_V1C ) \ + || defined( ASM_X86_V2C ) || defined( ASM_AMD64_C ) +# define EFUNCS_IN_C ENC_KEYING_IN_C +#elif !defined( ASM_X86_V2 ) +# define EFUNCS_IN_C ( ENCRYPTION_IN_C | ENC_KEYING_IN_C ) +#else +# define EFUNCS_IN_C 0 +#endif + +#if !defined( AES_DECRYPT ) +# define DFUNCS_IN_C 0 +#elif defined( ASSUME_VIA_ACE_PRESENT ) || defined( ASM_X86_V1C ) \ + || defined( ASM_X86_V2C ) || defined( ASM_AMD64_C ) +# define DFUNCS_IN_C DEC_KEYING_IN_C +#elif !defined( ASM_X86_V2 ) +# define DFUNCS_IN_C ( DECRYPTION_IN_C | DEC_KEYING_IN_C ) +#else +# define DFUNCS_IN_C 0 +#endif + +#define FUNCS_IN_C ( EFUNCS_IN_C | DFUNCS_IN_C ) + +/* END OF CONFIGURATION OPTIONS */ + +#define RC_LENGTH (5 * (AES_BLOCK_SIZE / 4 - 2)) + +/* Disable or report errors on some combinations of options */ + +#if ENC_ROUND == NO_TABLES && LAST_ENC_ROUND != NO_TABLES +# undef LAST_ENC_ROUND +# define LAST_ENC_ROUND NO_TABLES +#elif ENC_ROUND == ONE_TABLE && LAST_ENC_ROUND == FOUR_TABLES +# undef LAST_ENC_ROUND +# define LAST_ENC_ROUND ONE_TABLE +#endif + +#if ENC_ROUND == NO_TABLES && ENC_UNROLL != NONE +# undef ENC_UNROLL +# define ENC_UNROLL NONE +#endif + +#if DEC_ROUND == NO_TABLES && LAST_DEC_ROUND != NO_TABLES +# undef LAST_DEC_ROUND +# define LAST_DEC_ROUND NO_TABLES +#elif DEC_ROUND == ONE_TABLE && LAST_DEC_ROUND == FOUR_TABLES +# undef LAST_DEC_ROUND +# define LAST_DEC_ROUND ONE_TABLE +#endif + +#if DEC_ROUND == NO_TABLES && DEC_UNROLL != NONE +# undef DEC_UNROLL +# define DEC_UNROLL NONE +#endif + +#if defined( bswap32 ) +# define aes_sw32 bswap32 +#elif defined( bswap_32 ) +# define aes_sw32 bswap_32 +#else +# define brot(x,n) (((uint_32t)(x) << n) | ((uint_32t)(x) >> (32 - n))) +# define aes_sw32(x) ((brot((x),8) & 0x00ff00ff) | (brot((x),24) & 0xff00ff00)) +#endif + +/* upr(x,n): rotates bytes within words by n positions, moving bytes to + higher index positions with wrap around into low positions + ups(x,n): moves bytes by n positions to higher index positions in + words but without wrap around + bval(x,n): extracts a byte from a word + + WARNING: The definitions given here are intended only for use with + unsigned variables and with shift counts that are compile + time constants +*/ + +#if ( ALGORITHM_BYTE_ORDER == IS_LITTLE_ENDIAN ) +# define upr(x,n) (((uint_32t)(x) << (8 * (n))) | ((uint_32t)(x) >> (32 - 8 * (n)))) +# define ups(x,n) ((uint_32t) (x) << (8 * (n))) +# define bval(x,n) to_byte((x) >> (8 * (n))) +# define bytes2word(b0, b1, b2, b3) \ + (((uint_32t)(b3) << 24) | ((uint_32t)(b2) << 16) | ((uint_32t)(b1) << 8) | (b0)) +#endif + +#if ( ALGORITHM_BYTE_ORDER == IS_BIG_ENDIAN ) +# define upr(x,n) (((uint_32t)(x) >> (8 * (n))) | ((uint_32t)(x) << (32 - 8 * (n)))) +# define ups(x,n) ((uint_32t) (x) >> (8 * (n))) +# define bval(x,n) to_byte((x) >> (24 - 8 * (n))) +# define bytes2word(b0, b1, b2, b3) \ + (((uint_32t)(b0) << 24) | ((uint_32t)(b1) << 16) | ((uint_32t)(b2) << 8) | (b3)) +#endif + +#if defined( SAFE_IO ) +# define word_in(x,c) bytes2word(((const uint_8t*)(x)+4*c)[0], ((const uint_8t*)(x)+4*c)[1], \ + ((const uint_8t*)(x)+4*c)[2], ((const uint_8t*)(x)+4*c)[3]) +# define word_out(x,c,v) { ((uint_8t*)(x)+4*c)[0] = bval(v,0); ((uint_8t*)(x)+4*c)[1] = bval(v,1); \ + ((uint_8t*)(x)+4*c)[2] = bval(v,2); ((uint_8t*)(x)+4*c)[3] = bval(v,3); } +#elif ( ALGORITHM_BYTE_ORDER == PLATFORM_BYTE_ORDER ) +# define word_in(x,c) (*((uint_32t*)(x)+(c))) +# define word_out(x,c,v) (*((uint_32t*)(x)+(c)) = (v)) +#else +# define word_in(x,c) aes_sw32(*((uint_32t*)(x)+(c))) +# define word_out(x,c,v) (*((uint_32t*)(x)+(c)) = aes_sw32(v)) +#endif + +/* the finite field modular polynomial and elements */ + +#define WPOLY 0x011b +#define BPOLY 0x1b + +/* multiply four bytes in GF(2^8) by 'x' {02} in parallel */ + +#define m1 0x80808080 +#define m2 0x7f7f7f7f +#define gf_mulx(x) ((((x) & m2) << 1) ^ ((((x) & m1) >> 7) * BPOLY)) + +/* The following defines provide alternative definitions of gf_mulx that might + give improved performance if a fast 32-bit multiply is not available. Note + that a temporary variable u needs to be defined where gf_mulx is used. + +#define gf_mulx(x) (u = (x) & m1, u |= (u >> 1), ((x) & m2) << 1) ^ ((u >> 3) | (u >> 6)) +#define m4 (0x01010101 * BPOLY) +#define gf_mulx(x) (u = (x) & m1, ((x) & m2) << 1) ^ ((u - (u >> 7)) & m4) +*/ + +/* Work out which tables are needed for the different options */ + +#if defined( ASM_X86_V1C ) +# if defined( ENC_ROUND ) +# undef ENC_ROUND +# endif +# define ENC_ROUND FOUR_TABLES +# if defined( LAST_ENC_ROUND ) +# undef LAST_ENC_ROUND +# endif +# define LAST_ENC_ROUND FOUR_TABLES +# if defined( DEC_ROUND ) +# undef DEC_ROUND +# endif +# define DEC_ROUND FOUR_TABLES +# if defined( LAST_DEC_ROUND ) +# undef LAST_DEC_ROUND +# endif +# define LAST_DEC_ROUND FOUR_TABLES +# if defined( KEY_SCHED ) +# undef KEY_SCHED +# define KEY_SCHED FOUR_TABLES +# endif +#endif + +#if ( FUNCS_IN_C & ENCRYPTION_IN_C ) || defined( ASM_X86_V1C ) +# if ENC_ROUND == ONE_TABLE +# define FT1_SET +# elif ENC_ROUND == FOUR_TABLES +# define FT4_SET +# else +# define SBX_SET +# endif +# if LAST_ENC_ROUND == ONE_TABLE +# define FL1_SET +# elif LAST_ENC_ROUND == FOUR_TABLES +# define FL4_SET +# elif !defined( SBX_SET ) +# define SBX_SET +# endif +#endif + +#if ( FUNCS_IN_C & DECRYPTION_IN_C ) || defined( ASM_X86_V1C ) +# if DEC_ROUND == ONE_TABLE +# define IT1_SET +# elif DEC_ROUND == FOUR_TABLES +# define IT4_SET +# else +# define ISB_SET +# endif +# if LAST_DEC_ROUND == ONE_TABLE +# define IL1_SET +# elif LAST_DEC_ROUND == FOUR_TABLES +# define IL4_SET +# elif !defined(ISB_SET) +# define ISB_SET +# endif +#endif + +#if !(defined( REDUCE_CODE_SIZE ) && (defined( ASM_X86_V2 ) || defined( ASM_X86_V2C ))) +# if ((FUNCS_IN_C & ENC_KEYING_IN_C) || (FUNCS_IN_C & DEC_KEYING_IN_C)) +# if KEY_SCHED == ONE_TABLE +# if !defined( FL1_SET ) && !defined( FL4_SET ) +# define LS1_SET +# endif +# elif KEY_SCHED == FOUR_TABLES +# if !defined( FL4_SET ) +# define LS4_SET +# endif +# elif !defined( SBX_SET ) +# define SBX_SET +# endif +# endif +# if (FUNCS_IN_C & DEC_KEYING_IN_C) +# if KEY_SCHED == ONE_TABLE +# define IM1_SET +# elif KEY_SCHED == FOUR_TABLES +# define IM4_SET +# elif !defined( SBX_SET ) +# define SBX_SET +# endif +# endif +#endif + +/* generic definitions of Rijndael macros that use tables */ + +#define no_table(x,box,vf,rf,c) bytes2word( \ + box[bval(vf(x,0,c),rf(0,c))], \ + box[bval(vf(x,1,c),rf(1,c))], \ + box[bval(vf(x,2,c),rf(2,c))], \ + box[bval(vf(x,3,c),rf(3,c))]) + +#define one_table(x,op,tab,vf,rf,c) \ + ( tab[bval(vf(x,0,c),rf(0,c))] \ + ^ op(tab[bval(vf(x,1,c),rf(1,c))],1) \ + ^ op(tab[bval(vf(x,2,c),rf(2,c))],2) \ + ^ op(tab[bval(vf(x,3,c),rf(3,c))],3)) + +#define four_tables(x,tab,vf,rf,c) \ + ( tab[0][bval(vf(x,0,c),rf(0,c))] \ + ^ tab[1][bval(vf(x,1,c),rf(1,c))] \ + ^ tab[2][bval(vf(x,2,c),rf(2,c))] \ + ^ tab[3][bval(vf(x,3,c),rf(3,c))]) + +#define vf1(x,r,c) (x) +#define rf1(r,c) (r) +#define rf2(r,c) ((8+r-c)&3) + +/* perform forward and inverse column mix operation on four bytes in long word x in */ +/* parallel. NOTE: x must be a simple variable, NOT an expression in these macros. */ + +#if !(defined( REDUCE_CODE_SIZE ) && (defined( ASM_X86_V2 ) || defined( ASM_X86_V2C ))) + +#if defined( FM4_SET ) /* not currently used */ +# define fwd_mcol(x) four_tables(x,t_use(f,m),vf1,rf1,0) +#elif defined( FM1_SET ) /* not currently used */ +# define fwd_mcol(x) one_table(x,upr,t_use(f,m),vf1,rf1,0) +#else +# define dec_fmvars uint_32t g2 +# define fwd_mcol(x) (g2 = gf_mulx(x), g2 ^ upr((x) ^ g2, 3) ^ upr((x), 2) ^ upr((x), 1)) +#endif + +#if defined( IM4_SET ) +# define inv_mcol(x) four_tables(x,t_use(i,m),vf1,rf1,0) +#elif defined( IM1_SET ) +# define inv_mcol(x) one_table(x,upr,t_use(i,m),vf1,rf1,0) +#else +# define dec_imvars uint_32t g2, g4, g9 +# define inv_mcol(x) (g2 = gf_mulx(x), g4 = gf_mulx(g2), g9 = (x) ^ gf_mulx(g4), g4 ^= g9, \ + (x) ^ g2 ^ g4 ^ upr(g2 ^ g9, 3) ^ upr(g4, 2) ^ upr(g9, 1)) +#endif + +#if defined( FL4_SET ) +# define ls_box(x,c) four_tables(x,t_use(f,l),vf1,rf2,c) +#elif defined( LS4_SET ) +# define ls_box(x,c) four_tables(x,t_use(l,s),vf1,rf2,c) +#elif defined( FL1_SET ) +# define ls_box(x,c) one_table(x,upr,t_use(f,l),vf1,rf2,c) +#elif defined( LS1_SET ) +# define ls_box(x,c) one_table(x,upr,t_use(l,s),vf1,rf2,c) +#else +# define ls_box(x,c) no_table(x,t_use(s,box),vf1,rf2,c) +#endif + +#endif + +#if defined( ASM_X86_V1C ) && defined( AES_DECRYPT ) && !defined( ISB_SET ) +# define ISB_SET +#endif + +#endif diff --git a/src/java/KP2AKdbLibrary/jni/aes/aestab.c b/src/java/KP2AKdbLibrary/jni/aes/aestab.c new file mode 100644 index 00000000..1ac3739c --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/aestab.c @@ -0,0 +1,398 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 20/12/2007 +*/ + +#define DO_TABLES + +#include "aes.h" +#include "aesopt.h" + +#if defined(FIXED_TABLES) + +#define sb_data(w) {\ + w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5),\ + w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76),\ + w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0),\ + w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0),\ + w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc),\ + w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15),\ + w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a),\ + w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),\ + w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0),\ + w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84),\ + w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b),\ + w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf),\ + w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85),\ + w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8),\ + w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),\ + w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2),\ + w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17),\ + w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73),\ + w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88),\ + w(0x46), w(0xee), w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb),\ + w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c),\ + w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),\ + w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e), w(0xa9),\ + w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08),\ + w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6),\ + w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a),\ + w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e),\ + w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e),\ + w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),\ + w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf),\ + w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68),\ + w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), w(0x54), w(0xbb), w(0x16) } + +#define isb_data(w) {\ + w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38),\ + w(0xbf), w(0x40), w(0xa3), w(0x9e), w(0x81), w(0xf3), w(0xd7), w(0xfb),\ + w(0x7c), w(0xe3), w(0x39), w(0x82), w(0x9b), w(0x2f), w(0xff), w(0x87),\ + w(0x34), w(0x8e), w(0x43), w(0x44), w(0xc4), w(0xde), w(0xe9), w(0xcb),\ + w(0x54), w(0x7b), w(0x94), w(0x32), w(0xa6), w(0xc2), w(0x23), w(0x3d),\ + w(0xee), w(0x4c), w(0x95), w(0x0b), w(0x42), w(0xfa), w(0xc3), w(0x4e),\ + w(0x08), w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), w(0xb2),\ + w(0x76), w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), w(0x25),\ + w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98), w(0x16),\ + w(0xd4), w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65), w(0xb6), w(0x92),\ + w(0x6c), w(0x70), w(0x48), w(0x50), w(0xfd), w(0xed), w(0xb9), w(0xda),\ + w(0x5e), w(0x15), w(0x46), w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84),\ + w(0x90), w(0xd8), w(0xab), w(0x00), w(0x8c), w(0xbc), w(0xd3), w(0x0a),\ + w(0xf7), w(0xe4), w(0x58), w(0x05), w(0xb8), w(0xb3), w(0x45), w(0x06),\ + w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), w(0x0f), w(0x02),\ + w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a), w(0x6b),\ + w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), w(0x67), w(0xdc), w(0xea),\ + w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0), w(0xb4), w(0xe6), w(0x73),\ + w(0x96), w(0xac), w(0x74), w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85),\ + w(0xe2), w(0xf9), w(0x37), w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e),\ + w(0x47), w(0xf1), w(0x1a), w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89),\ + w(0x6f), w(0xb7), w(0x62), w(0x0e), w(0xaa), w(0x18), w(0xbe), w(0x1b),\ + w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), w(0xd2), w(0x79), w(0x20),\ + w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd), w(0x5a), w(0xf4),\ + w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88), w(0x07), w(0xc7), w(0x31),\ + w(0xb1), w(0x12), w(0x10), w(0x59), w(0x27), w(0x80), w(0xec), w(0x5f),\ + w(0x60), w(0x51), w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d),\ + w(0x2d), w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef),\ + w(0xa0), w(0xe0), w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), w(0xb0),\ + w(0xc8), w(0xeb), w(0xbb), w(0x3c), w(0x83), w(0x53), w(0x99), w(0x61),\ + w(0x17), w(0x2b), w(0x04), w(0x7e), w(0xba), w(0x77), w(0xd6), w(0x26),\ + w(0xe1), w(0x69), w(0x14), w(0x63), w(0x55), w(0x21), w(0x0c), w(0x7d) } + +#define mm_data(w) {\ + w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07),\ + w(0x08), w(0x09), w(0x0a), w(0x0b), w(0x0c), w(0x0d), w(0x0e), w(0x0f),\ + w(0x10), w(0x11), w(0x12), w(0x13), w(0x14), w(0x15), w(0x16), w(0x17),\ + w(0x18), w(0x19), w(0x1a), w(0x1b), w(0x1c), w(0x1d), w(0x1e), w(0x1f),\ + w(0x20), w(0x21), w(0x22), w(0x23), w(0x24), w(0x25), w(0x26), w(0x27),\ + w(0x28), w(0x29), w(0x2a), w(0x2b), w(0x2c), w(0x2d), w(0x2e), w(0x2f),\ + w(0x30), w(0x31), w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), w(0x37),\ + w(0x38), w(0x39), w(0x3a), w(0x3b), w(0x3c), w(0x3d), w(0x3e), w(0x3f),\ + w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46), w(0x47),\ + w(0x48), w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d), w(0x4e), w(0x4f),\ + w(0x50), w(0x51), w(0x52), w(0x53), w(0x54), w(0x55), w(0x56), w(0x57),\ + w(0x58), w(0x59), w(0x5a), w(0x5b), w(0x5c), w(0x5d), w(0x5e), w(0x5f),\ + w(0x60), w(0x61), w(0x62), w(0x63), w(0x64), w(0x65), w(0x66), w(0x67),\ + w(0x68), w(0x69), w(0x6a), w(0x6b), w(0x6c), w(0x6d), w(0x6e), w(0x6f),\ + w(0x70), w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), w(0x76), w(0x77),\ + w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e), w(0x7f),\ + w(0x80), w(0x81), w(0x82), w(0x83), w(0x84), w(0x85), w(0x86), w(0x87),\ + w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c), w(0x8d), w(0x8e), w(0x8f),\ + w(0x90), w(0x91), w(0x92), w(0x93), w(0x94), w(0x95), w(0x96), w(0x97),\ + w(0x98), w(0x99), w(0x9a), w(0x9b), w(0x9c), w(0x9d), w(0x9e), w(0x9f),\ + w(0xa0), w(0xa1), w(0xa2), w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7),\ + w(0xa8), w(0xa9), w(0xaa), w(0xab), w(0xac), w(0xad), w(0xae), w(0xaf),\ + w(0xb0), w(0xb1), w(0xb2), w(0xb3), w(0xb4), w(0xb5), w(0xb6), w(0xb7),\ + w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd), w(0xbe), w(0xbf),\ + w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4), w(0xc5), w(0xc6), w(0xc7),\ + w(0xc8), w(0xc9), w(0xca), w(0xcb), w(0xcc), w(0xcd), w(0xce), w(0xcf),\ + w(0xd0), w(0xd1), w(0xd2), w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7),\ + w(0xd8), w(0xd9), w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf),\ + w(0xe0), w(0xe1), w(0xe2), w(0xe3), w(0xe4), w(0xe5), w(0xe6), w(0xe7),\ + w(0xe8), w(0xe9), w(0xea), w(0xeb), w(0xec), w(0xed), w(0xee), w(0xef),\ + w(0xf0), w(0xf1), w(0xf2), w(0xf3), w(0xf4), w(0xf5), w(0xf6), w(0xf7),\ + w(0xf8), w(0xf9), w(0xfa), w(0xfb), w(0xfc), w(0xfd), w(0xfe), w(0xff) } + +#define rc_data(w) {\ + w(0x01), w(0x02), w(0x04), w(0x08), w(0x10),w(0x20), w(0x40), w(0x80),\ + w(0x1b), w(0x36) } + +#define h0(x) (x) + +#define w0(p) bytes2word(p, 0, 0, 0) +#define w1(p) bytes2word(0, p, 0, 0) +#define w2(p) bytes2word(0, 0, p, 0) +#define w3(p) bytes2word(0, 0, 0, p) + +#define u0(p) bytes2word(f2(p), p, p, f3(p)) +#define u1(p) bytes2word(f3(p), f2(p), p, p) +#define u2(p) bytes2word(p, f3(p), f2(p), p) +#define u3(p) bytes2word(p, p, f3(p), f2(p)) + +#define v0(p) bytes2word(fe(p), f9(p), fd(p), fb(p)) +#define v1(p) bytes2word(fb(p), fe(p), f9(p), fd(p)) +#define v2(p) bytes2word(fd(p), fb(p), fe(p), f9(p)) +#define v3(p) bytes2word(f9(p), fd(p), fb(p), fe(p)) + +#endif + +#if defined(FIXED_TABLES) || !defined(FF_TABLES) + +#define f2(x) ((x<<1) ^ (((x>>7) & 1) * WPOLY)) +#define f4(x) ((x<<2) ^ (((x>>6) & 1) * WPOLY) ^ (((x>>6) & 2) * WPOLY)) +#define f8(x) ((x<<3) ^ (((x>>5) & 1) * WPOLY) ^ (((x>>5) & 2) * WPOLY) \ + ^ (((x>>5) & 4) * WPOLY)) +#define f3(x) (f2(x) ^ x) +#define f9(x) (f8(x) ^ x) +#define fb(x) (f8(x) ^ f2(x) ^ x) +#define fd(x) (f8(x) ^ f4(x) ^ x) +#define fe(x) (f8(x) ^ f4(x) ^ f2(x)) + +#else + +#define f2(x) ((x) ? pow[log[x] + 0x19] : 0) +#define f3(x) ((x) ? pow[log[x] + 0x01] : 0) +#define f9(x) ((x) ? pow[log[x] + 0xc7] : 0) +#define fb(x) ((x) ? pow[log[x] + 0x68] : 0) +#define fd(x) ((x) ? pow[log[x] + 0xee] : 0) +#define fe(x) ((x) ? pow[log[x] + 0xdf] : 0) + +#endif + +#include "aestab.h" + +#if defined(__cplusplus) +extern "C" +{ +#endif + +#if defined(FIXED_TABLES) + +/* implemented in case of wrong call for fixed tables */ + +AES_RETURN aes_init(void) +{ + return EXIT_SUCCESS; +} + +#else /* Generate the tables for the dynamic table option */ + +#if defined(FF_TABLES) + +#define gf_inv(x) ((x) ? pow[ 255 - log[x]] : 0) + +#else + +/* It will generally be sensible to use tables to compute finite + field multiplies and inverses but where memory is scarse this + code might sometimes be better. But it only has effect during + initialisation so its pretty unimportant in overall terms. +*/ + +/* return 2 ^ (n - 1) where n is the bit number of the highest bit + set in x with x in the range 1 < x < 0x00000200. This form is + used so that locals within fi can be bytes rather than words +*/ + +static uint_8t hibit(const uint_32t x) +{ uint_8t r = (uint_8t)((x >> 1) | (x >> 2)); + + r |= (r >> 2); + r |= (r >> 4); + return (r + 1) >> 1; +} + +/* return the inverse of the finite field element x */ + +static uint_8t gf_inv(const uint_8t x) +{ uint_8t p1 = x, p2 = BPOLY, n1 = hibit(x), n2 = 0x80, v1 = 1, v2 = 0; + + if(x < 2) + return x; + + for( ; ; ) + { + if(n1) + while(n2 >= n1) /* divide polynomial p2 by p1 */ + { + n2 /= n1; /* shift smaller polynomial left */ + p2 ^= (p1 * n2) & 0xff; /* and remove from larger one */ + v2 ^= v1 * n2; /* shift accumulated value and */ + n2 = hibit(p2); /* add into result */ + } + else + return v1; + + if(n2) /* repeat with values swapped */ + while(n1 >= n2) + { + n1 /= n2; + p1 ^= p2 * n1; + v1 ^= v2 * n1; + n1 = hibit(p1); + } + else + return v2; + } +} + +#endif + +/* The forward and inverse affine transformations used in the S-box */ +uint_8t fwd_affine(const uint_8t x) +{ uint_32t w = x; + w ^= (w << 1) ^ (w << 2) ^ (w << 3) ^ (w << 4); + return 0x63 ^ ((w ^ (w >> 8)) & 0xff); +} + +uint_8t inv_affine(const uint_8t x) +{ uint_32t w = x; + w = (w << 1) ^ (w << 3) ^ (w << 6); + return 0x05 ^ ((w ^ (w >> 8)) & 0xff); +} + +static int init = 0; + +AES_RETURN aes_init(void) +{ uint_32t i, w; + +#if defined(FF_TABLES) + + uint_8t pow[512], log[256]; + + if(init) + return EXIT_SUCCESS; + /* log and power tables for GF(2^8) finite field with + WPOLY as modular polynomial - the simplest primitive + root is 0x03, used here to generate the tables + */ + + i = 0; w = 1; + do + { + pow[i] = (uint_8t)w; + pow[i + 255] = (uint_8t)w; + log[w] = (uint_8t)i++; + w ^= (w << 1) ^ (w & 0x80 ? WPOLY : 0); + } + while (w != 1); + +#else + if(init) + return EXIT_SUCCESS; +#endif + + for(i = 0, w = 1; i < RC_LENGTH; ++i) + { + t_set(r,c)[i] = bytes2word(w, 0, 0, 0); + w = f2(w); + } + + for(i = 0; i < 256; ++i) + { uint_8t b; + + b = fwd_affine(gf_inv((uint_8t)i)); + w = bytes2word(f2(b), b, b, f3(b)); + +#if defined( SBX_SET ) + t_set(s,box)[i] = b; +#endif + +#if defined( FT1_SET ) /* tables for a normal encryption round */ + t_set(f,n)[i] = w; +#endif +#if defined( FT4_SET ) + t_set(f,n)[0][i] = w; + t_set(f,n)[1][i] = upr(w,1); + t_set(f,n)[2][i] = upr(w,2); + t_set(f,n)[3][i] = upr(w,3); +#endif + w = bytes2word(b, 0, 0, 0); + +#if defined( FL1_SET ) /* tables for last encryption round (may also */ + t_set(f,l)[i] = w; /* be used in the key schedule) */ +#endif +#if defined( FL4_SET ) + t_set(f,l)[0][i] = w; + t_set(f,l)[1][i] = upr(w,1); + t_set(f,l)[2][i] = upr(w,2); + t_set(f,l)[3][i] = upr(w,3); +#endif + +#if defined( LS1_SET ) /* table for key schedule if t_set(f,l) above is*/ + t_set(l,s)[i] = w; /* not of the required form */ +#endif +#if defined( LS4_SET ) + t_set(l,s)[0][i] = w; + t_set(l,s)[1][i] = upr(w,1); + t_set(l,s)[2][i] = upr(w,2); + t_set(l,s)[3][i] = upr(w,3); +#endif + + b = gf_inv(inv_affine((uint_8t)i)); + w = bytes2word(fe(b), f9(b), fd(b), fb(b)); + +#if defined( IM1_SET ) /* tables for the inverse mix column operation */ + t_set(i,m)[b] = w; +#endif +#if defined( IM4_SET ) + t_set(i,m)[0][b] = w; + t_set(i,m)[1][b] = upr(w,1); + t_set(i,m)[2][b] = upr(w,2); + t_set(i,m)[3][b] = upr(w,3); +#endif + +#if defined( ISB_SET ) + t_set(i,box)[i] = b; +#endif +#if defined( IT1_SET ) /* tables for a normal decryption round */ + t_set(i,n)[i] = w; +#endif +#if defined( IT4_SET ) + t_set(i,n)[0][i] = w; + t_set(i,n)[1][i] = upr(w,1); + t_set(i,n)[2][i] = upr(w,2); + t_set(i,n)[3][i] = upr(w,3); +#endif + w = bytes2word(b, 0, 0, 0); +#if defined( IL1_SET ) /* tables for last decryption round */ + t_set(i,l)[i] = w; +#endif +#if defined( IL4_SET ) + t_set(i,l)[0][i] = w; + t_set(i,l)[1][i] = upr(w,1); + t_set(i,l)[2][i] = upr(w,2); + t_set(i,l)[3][i] = upr(w,3); +#endif + } + init = 1; + return EXIT_SUCCESS; +} + +#endif + +#if defined(__cplusplus) +} +#endif + diff --git a/src/java/KP2AKdbLibrary/jni/aes/aestab.h b/src/java/KP2AKdbLibrary/jni/aes/aestab.h new file mode 100644 index 00000000..6b573cfc --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/aestab.h @@ -0,0 +1,180 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 20/12/2007 + + This file contains the code for declaring the tables needed to implement + AES. The file aesopt.h is assumed to be included before this header file. + If there are no global variables, the definitions here can be used to put + the AES tables in a structure so that a pointer can then be added to the + AES context to pass them to the AES routines that need them. If this + facility is used, the calling program has to ensure that this pointer is + managed appropriately. In particular, the value of the t_dec(in,it) item + in the table structure must be set to zero in order to ensure that the + tables are initialised. In practice the three code sequences in aeskey.c + that control the calls to aes_init() and the aes_init() routine itself will + have to be changed for a specific implementation. If global variables are + available it will generally be preferable to use them with the precomputed + FIXED_TABLES option that uses static global tables. + + The following defines can be used to control the way the tables + are defined, initialised and used in embedded environments that + require special features for these purposes + + the 't_dec' construction is used to declare fixed table arrays + the 't_set' construction is used to set fixed table values + the 't_use' construction is used to access fixed table values + + 256 byte tables: + + t_xxx(s,box) => forward S box + t_xxx(i,box) => inverse S box + + 256 32-bit word OR 4 x 256 32-bit word tables: + + t_xxx(f,n) => forward normal round + t_xxx(f,l) => forward last round + t_xxx(i,n) => inverse normal round + t_xxx(i,l) => inverse last round + t_xxx(l,s) => key schedule table + t_xxx(i,m) => key schedule table + + Other variables and tables: + + t_xxx(r,c) => the rcon table +*/ + +#if !defined( _AESTAB_H ) +#define _AESTAB_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#define t_dec(m,n) t_##m##n +#define t_set(m,n) t_##m##n +#define t_use(m,n) t_##m##n + +#if defined(FIXED_TABLES) +# if !defined( __GNUC__ ) && (defined( __MSDOS__ ) || defined( __WIN16__ )) +/* make tables far data to avoid using too much DGROUP space (PG) */ +# define CONST const far +# else +# define CONST const +# endif +#else +# define CONST +#endif + +#if defined(DO_TABLES) +# define EXTERN +#else +# define EXTERN extern +#endif + +#if defined(_MSC_VER) && defined(TABLE_ALIGN) +#define ALIGN __declspec(align(TABLE_ALIGN)) +#else +#define ALIGN +#endif + +#if defined( __WATCOMC__ ) && ( __WATCOMC__ >= 1100 ) +# define XP_DIR __cdecl +#else +# define XP_DIR +#endif + +#if defined(DO_TABLES) && defined(FIXED_TABLES) +#define d_1(t,n,b,e) EXTERN ALIGN CONST XP_DIR t n[256] = b(e) +#define d_4(t,n,b,e,f,g,h) EXTERN ALIGN CONST XP_DIR t n[4][256] = { b(e), b(f), b(g), b(h) } +EXTERN ALIGN CONST uint_32t t_dec(r,c)[RC_LENGTH] = rc_data(w0); +#else +#define d_1(t,n,b,e) EXTERN ALIGN CONST XP_DIR t n[256] +#define d_4(t,n,b,e,f,g,h) EXTERN ALIGN CONST XP_DIR t n[4][256] +EXTERN ALIGN CONST uint_32t t_dec(r,c)[RC_LENGTH]; +#endif + +#if defined( SBX_SET ) + d_1(uint_8t, t_dec(s,box), sb_data, h0); +#endif +#if defined( ISB_SET ) + d_1(uint_8t, t_dec(i,box), isb_data, h0); +#endif + +#if defined( FT1_SET ) + d_1(uint_32t, t_dec(f,n), sb_data, u0); +#endif +#if defined( FT4_SET ) + d_4(uint_32t, t_dec(f,n), sb_data, u0, u1, u2, u3); +#endif + +#if defined( FL1_SET ) + d_1(uint_32t, t_dec(f,l), sb_data, w0); +#endif +#if defined( FL4_SET ) + d_4(uint_32t, t_dec(f,l), sb_data, w0, w1, w2, w3); +#endif + +#if defined( IT1_SET ) + d_1(uint_32t, t_dec(i,n), isb_data, v0); +#endif +#if defined( IT4_SET ) + d_4(uint_32t, t_dec(i,n), isb_data, v0, v1, v2, v3); +#endif + +#if defined( IL1_SET ) + d_1(uint_32t, t_dec(i,l), isb_data, w0); +#endif +#if defined( IL4_SET ) + d_4(uint_32t, t_dec(i,l), isb_data, w0, w1, w2, w3); +#endif + +#if defined( LS1_SET ) +#if defined( FL1_SET ) +#undef LS1_SET +#else + d_1(uint_32t, t_dec(l,s), sb_data, w0); +#endif +#endif + +#if defined( LS4_SET ) +#if defined( FL4_SET ) +#undef LS4_SET +#else + d_4(uint_32t, t_dec(l,s), sb_data, w0, w1, w2, w3); +#endif +#endif + +#if defined( IM1_SET ) + d_1(uint_32t, t_dec(i,m), mm_data, v0); +#endif +#if defined( IM4_SET ) + d_4(uint_32t, t_dec(i,m), mm_data, v0, v1, v2, v3); +#endif + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/src/java/KP2AKdbLibrary/jni/aes/aesxam.c b/src/java/KP2AKdbLibrary/jni/aes/aesxam.c new file mode 100644 index 00000000..e454aefc --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/aesxam.c @@ -0,0 +1,426 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 20/12/2007 +*/ + +// An example of the use of AES (Rijndael) for file encryption. This code +// implements AES in CBC mode with ciphertext stealing when the file length +// is greater than one block (16 bytes). This code is an example of how to +// use AES and is not intended for real use since it does not provide any +// file integrity checking. +// +// The Command line is: +// +// aesxam input_file_name output_file_name [D|E] hexadecimalkey +// +// where E gives encryption and D decryption of the input file into the +// output file using the given hexadecimal key string. The later is a +// hexadecimal sequence of 32, 48 or 64 digits. Examples to encrypt or +// decrypt aes.c into aes.enc are: +// +// aesxam file.c file.enc E 0123456789abcdeffedcba9876543210 +// +// aesxam file.enc file2.c D 0123456789abcdeffedcba9876543210 +// +// which should return a file 'file2.c' identical to 'file.c' +// +// CIPHERTEXT STEALING +// +// Ciphertext stealing modifies the encryption of the last two CBC +// blocks. It can be applied invariably to the last two plaintext +// blocks or only applied when the last block is a partial one. In +// this code it is only applied if there is a partial block. For +// a plaintext consisting of N blocks, with the last block possibly +// a partial one, ciphertext stealing works as shown below (note the +// reversal of the last two ciphertext blocks). During decryption +// the part of the C:N-1 block that is not transmitted (X) can be +// obtained from the decryption of the penultimate ciphertext block +// since the bytes in X are xored with the zero padding appended to +// the last plaintext block. +// +// This is a picture of the processing of the last +// plaintext blocks during encryption: +// +// +---------+ +---------+ +---------+ +-------+-+ +// | P:N-4 | | P:N-3 | | P:N-2 | | P:N-1 |0| +// +---------+ +---------+ +---------+ +-------+-+ +// | | | | +// v v v v +// +----->x +----->x +----->x +----->x x = xor +// | | | | | | | | +// | v | v | v | v +// | +---+ | +---+ | +---+ | +---+ +// | | E | | | E | | | E | | | E | +// | +---+ | +---+ | +---+ | +---+ +// | | | | | | | | +// | | | | | v | +---+ +// | | | | | +-------+-+ | | +// | | | | | | C:N-1 |X| | | +// | | | | | +-------+-+ ^ | +// | | | | | || | | +// | | | | | |+------+ | +// | | | | | +----------|--+ +// | | | | | | | +// | | | | | +---------+ | +// | | | | | | | +// | v | v | v v +// | +---------+ | +---------+ | +---------+ +-------+ +// -+ | C:N-4 |-+ | C:N-3 |-+ | C:N-2 | | C:N-1 | +// +---------+ +---------+ +---------+ +-------+ +// +// And this is a picture of the processing of the last +// ciphertext blocks during decryption: +// +// +---------+ +---------+ +---------+ +-------+ +// -+ | C:N-4 |-+ | C:N-3 |-+ | C:N-2 | | C:N-1 | +// | +---------+ | +---------+ | +---------+ +-------+ +// | | | | | | | +// | v | v | v +--------|----+ +// | +---+ | +---+ | +---+ | +--<--+ | +// | | D | | | D | | | D | | | | | +// | +---+ | +---+ | +---+ | | v v +// | | | | | | ^ | +-------+-+ +// | v | v | v | | | C:N-1 |X| +// +----->x +----->x | +-------+-+ | +-------+-+ +// | | | | |X| | | +// | | | +-------+-+ | v +// | | | | | +---+ +// | | | | v | D | +// | | | +------>x +---+ +// | | | | | +// | | +----->x<-----|------+ x = xor +// | | | +-----+ +// | | | | +// v v v v +// +---------+ +---------+ +---------+ +-------+ +// | P:N-4 | | P:N-3 | | P:N-2 | | P:N-1 | +// +---------+ +---------+ +---------+ +-------+ + +#include +#include + +#include "aes.h" +#include "rdtsc.h" + +#define BLOCK_LEN 16 + +#define OK 0 +#define READ_ERROR -7 +#define WRITE_ERROR -8 + +// A Pseudo Random Number Generator (PRNG) used for the +// Initialisation Vector. The PRNG is George Marsaglia's +// Multiply-With-Carry (MWC) PRNG that concatenates two +// 16-bit MWC generators: +// x(n)=36969 * x(n-1) + carry mod 2^16 +// y(n)=18000 * y(n-1) + carry mod 2^16 +// to produce a combined PRNG with a period of about 2^60. +// The Pentium cycle counter is used to initialise it. This +// is crude but the IV does not really need to be secret. + +#define RAND(a,b) (((a = 36969 * (a & 65535) + (a >> 16)) << 16) + \ + (b = 18000 * (b & 65535) + (b >> 16)) ) + +void fillrand(unsigned char *buf, const int len) +{ static unsigned long a[2], mt = 1, count = 4; + static unsigned char r[4]; + int i; + + if(mt) { mt = 0; *(unsigned long long*)a = read_tsc(); } + + for(i = 0; i < len; ++i) + { + if(count == 4) + { + *(unsigned long*)r = RAND(a[0], a[1]); + count = 0; + } + + buf[i] = r[count++]; + } +} + +int encfile(FILE *fin, FILE *fout, aes_encrypt_ctx ctx[1]) +{ unsigned char dbuf[3 * BLOCK_LEN]; + unsigned long i, len, wlen = BLOCK_LEN; + + // When ciphertext stealing is used, we three ciphertext blocks so + // we use a buffer that is three times the block length. The buffer + // pointers b1, b2 and b3 point to the buffer positions of three + // ciphertext blocks, b3 being the most recent and b1 being the + // oldest. We start with the IV in b1 and the block to be decrypted + // in b2. + + // set a random IV + + fillrand(dbuf, BLOCK_LEN); + + // read the first file block + len = (unsigned long) fread((char*)dbuf + BLOCK_LEN, 1, BLOCK_LEN, fin); + + if(len < BLOCK_LEN) + { // if the file length is less than one block + + // xor the file bytes with the IV bytes + for(i = 0; i < len; ++i) + dbuf[i + BLOCK_LEN] ^= dbuf[i]; + + // encrypt the top 16 bytes of the buffer + aes_encrypt(dbuf + len, dbuf + len, ctx); + + len += BLOCK_LEN; + // write the IV and the encrypted file bytes + if(fwrite((char*)dbuf, 1, len, fout) != len) + return WRITE_ERROR; + + return OK; + } + else // if the file length is more 16 bytes + { unsigned char *b1 = dbuf, *b2 = b1 + BLOCK_LEN, *b3 = b2 + BLOCK_LEN, *bt; + + // write the IV + if(fwrite((char*)dbuf, 1, BLOCK_LEN, fout) != BLOCK_LEN) + return WRITE_ERROR; + + for( ; ; ) + { + // read the next block to see if ciphertext stealing is needed + len = (unsigned long)fread((char*)b3, 1, BLOCK_LEN, fin); + + // do CBC chaining prior to encryption for current block (in b2) + for(i = 0; i < BLOCK_LEN; ++i) + b1[i] ^= b2[i]; + + // encrypt the block (now in b1) + aes_encrypt(b1, b1, ctx); + + if(len != 0 && len != BLOCK_LEN) // use ciphertext stealing + { + // set the length of the last block + wlen = len; + + // xor ciphertext into last block + for(i = 0; i < len; ++i) + b3[i] ^= b1[i]; + + // move 'stolen' ciphertext into last block + for(i = len; i < BLOCK_LEN; ++i) + b3[i] = b1[i]; + + // encrypt this block + aes_encrypt(b3, b3, ctx); + + // and write it as the second to last encrypted block + if(fwrite((char*)b3, 1, BLOCK_LEN, fout) != BLOCK_LEN) + return WRITE_ERROR; + } + + // write the encrypted block + if(fwrite((char*)b1, 1, wlen, fout) != wlen) + return WRITE_ERROR; + + if(len != BLOCK_LEN) + return OK; + + // advance the buffer pointers + bt = b3, b3 = b2, b2 = b1, b1 = bt; + } + } +} + +int decfile(FILE *fin, FILE *fout, aes_decrypt_ctx ctx[1]) +{ unsigned char dbuf[3 * BLOCK_LEN], buf[BLOCK_LEN]; + unsigned long i, len, wlen = BLOCK_LEN; + + // When ciphertext stealing is used, we three ciphertext blocks so + // we use a buffer that is three times the block length. The buffer + // pointers b1, b2 and b3 point to the buffer positions of three + // ciphertext blocks, b3 being the most recent and b1 being the + // oldest. We start with the IV in b1 and the block to be decrypted + // in b2. + + len = (unsigned long)fread((char*)dbuf, 1, 2 * BLOCK_LEN, fin); + + if(len < 2 * BLOCK_LEN) // the original file is less than one block in length + { + len -= BLOCK_LEN; + // decrypt from position len to position len + BLOCK_LEN + aes_decrypt(dbuf + len, dbuf + len, ctx); + + // undo the CBC chaining + for(i = 0; i < len; ++i) + dbuf[i] ^= dbuf[i + BLOCK_LEN]; + + // output the decrypted bytes + if(fwrite((char*)dbuf, 1, len, fout) != len) + return WRITE_ERROR; + + return OK; + } + else + { unsigned char *b1 = dbuf, *b2 = b1 + BLOCK_LEN, *b3 = b2 + BLOCK_LEN, *bt; + + for( ; ; ) // while some ciphertext remains, prepare to decrypt block b2 + { + // read in the next block to see if ciphertext stealing is needed + len = fread((char*)b3, 1, BLOCK_LEN, fin); + + // decrypt the b2 block + aes_decrypt(b2, buf, ctx); + + if(len == 0 || len == BLOCK_LEN) // no ciphertext stealing + { + // unchain CBC using the previous ciphertext block in b1 + for(i = 0; i < BLOCK_LEN; ++i) + buf[i] ^= b1[i]; + } + else // partial last block - use ciphertext stealing + { + wlen = len; + + // produce last 'len' bytes of plaintext by xoring with + // the lowest 'len' bytes of next block b3 - C[N-1] + for(i = 0; i < len; ++i) + buf[i] ^= b3[i]; + + // reconstruct the C[N-1] block in b3 by adding in the + // last (BLOCK_LEN - len) bytes of C[N-2] in b2 + for(i = len; i < BLOCK_LEN; ++i) + b3[i] = buf[i]; + + // decrypt the C[N-1] block in b3 + aes_decrypt(b3, b3, ctx); + + // produce the last but one plaintext block by xoring with + // the last but two ciphertext block + for(i = 0; i < BLOCK_LEN; ++i) + b3[i] ^= b1[i]; + + // write decrypted plaintext blocks + if(fwrite((char*)b3, 1, BLOCK_LEN, fout) != BLOCK_LEN) + return WRITE_ERROR; + } + + // write the decrypted plaintext block + if(fwrite((char*)buf, 1, wlen, fout) != wlen) + return WRITE_ERROR; + + if(len != BLOCK_LEN) + return OK; + + // advance the buffer pointers + bt = b1, b1 = b2, b2 = b3, b3 = bt; + } + } +} + +int main(int argc, char *argv[]) +{ FILE *fin = 0, *fout = 0; + char *cp, ch, key[32]; + int i, by = 0, key_len, err = 0; + + if(argc != 5 || toupper(*argv[3]) != 'D' && toupper(*argv[3]) != 'E') + { + printf("usage: aesxam in_filename out_filename [d/e] key_in_hex\n"); + err = -1; goto exit; + } + + aes_init(); // in case dynamic AES tables are being used + + cp = argv[4]; // this is a pointer to the hexadecimal key digits + i = 0; // this is a count for the input digits processed + + while(i < 64 && *cp) // the maximum key length is 32 bytes and + { // hence at most 64 hexadecimal digits + ch = toupper(*cp++); // process a hexadecimal digit + if(ch >= '0' && ch <= '9') + by = (by << 4) + ch - '0'; + else if(ch >= 'A' && ch <= 'F') + by = (by << 4) + ch - 'A' + 10; + else // error if not hexadecimal + { + printf("key must be in hexadecimal notation\n"); + err = -2; goto exit; + } + + // store a key byte for each pair of hexadecimal digits + if(i++ & 1) + key[i / 2 - 1] = by & 0xff; + } + + if(*cp) + { + printf("The key value is too long\n"); + err = -3; goto exit; + } + else if(i < 32 || (i & 15)) + { + printf("The key length must be 32, 48 or 64 hexadecimal digits\n"); + err = -4; goto exit; + } + + key_len = i / 2; + + if(!(fin = fopen(argv[1], "rb"))) // try to open the input file + { + printf("The input file: %s could not be opened\n", argv[1]); + err = -5; goto exit; + } + + if(!(fout = fopen(argv[2], "wb"))) // try to open the output file + { + printf("The output file: %s could not be opened\n", argv[2]); + err = -6; goto exit; + } + + if(toupper(*argv[3]) == 'E') // encryption in Cipher Block Chaining mode + { aes_encrypt_ctx ctx[1]; + + aes_encrypt_key((unsigned char*)key, key_len, ctx); + + err = encfile(fin, fout, ctx); + } + else // decryption in Cipher Block Chaining mode + { aes_decrypt_ctx ctx[1]; + + aes_decrypt_key((unsigned char*)key, key_len, ctx); + + err = decfile(fin, fout, ctx); + } +exit: + if(err == READ_ERROR) + printf("Error reading from input file: %s\n", argv[1]); + + if(err == WRITE_ERROR) + printf("Error writing to output file: %s\n", argv[2]); + + if(fout) + fclose(fout); + + if(fin) + fclose(fin); + + return err; +} diff --git a/src/java/KP2AKdbLibrary/jni/aes/brg_endian.h b/src/java/KP2AKdbLibrary/jni/aes/brg_endian.h new file mode 100644 index 00000000..e3cf0d11 --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/brg_endian.h @@ -0,0 +1,133 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 20/12/2007 +*/ + +#ifndef _BRG_ENDIAN_H +#define _BRG_ENDIAN_H + +#define IS_BIG_ENDIAN 4321 /* byte 0 is most significant (mc68k) */ +#define IS_LITTLE_ENDIAN 1234 /* byte 0 is least significant (i386) */ + +/* Include files where endian defines and byteswap functions may reside */ +#if defined( __sun ) +# include +#elif defined( __FreeBSD__ ) || defined( __OpenBSD__ ) || defined( __NetBSD__ ) +# include +#elif defined( BSD ) && ( BSD >= 199103 ) || defined( __APPLE__ ) || \ + defined( __CYGWIN32__ ) || defined( __DJGPP__ ) || defined( __osf__ ) +# include +#elif defined( __linux__ ) || defined( __GNUC__ ) || defined( __GNU_LIBRARY__ ) +# if !defined( __MINGW32__ ) && !defined( _AIX ) +# include +# if !defined( __BEOS__ ) +# include +# endif +# endif +#endif + +/* Now attempt to set the define for platform byte order using any */ +/* of the four forms SYMBOL, _SYMBOL, __SYMBOL & __SYMBOL__, which */ +/* seem to encompass most endian symbol definitions */ + +#if defined( BIG_ENDIAN ) && defined( LITTLE_ENDIAN ) +# if defined( BYTE_ORDER ) && BYTE_ORDER == BIG_ENDIAN +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +# elif defined( BYTE_ORDER ) && BYTE_ORDER == LITTLE_ENDIAN +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +# endif +#elif defined( BIG_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#elif defined( LITTLE_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif + +#if defined( _BIG_ENDIAN ) && defined( _LITTLE_ENDIAN ) +# if defined( _BYTE_ORDER ) && _BYTE_ORDER == _BIG_ENDIAN +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +# elif defined( _BYTE_ORDER ) && _BYTE_ORDER == _LITTLE_ENDIAN +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +# endif +#elif defined( _BIG_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#elif defined( _LITTLE_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif + +#if defined( __BIG_ENDIAN ) && defined( __LITTLE_ENDIAN ) +# if defined( __BYTE_ORDER ) && __BYTE_ORDER == __BIG_ENDIAN +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +# elif defined( __BYTE_ORDER ) && __BYTE_ORDER == __LITTLE_ENDIAN +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +# endif +#elif defined( __BIG_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#elif defined( __LITTLE_ENDIAN ) +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif + +#if defined( __BIG_ENDIAN__ ) && defined( __LITTLE_ENDIAN__ ) +# if defined( __BYTE_ORDER__ ) && __BYTE_ORDER__ == __BIG_ENDIAN__ +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +# elif defined( __BYTE_ORDER__ ) && __BYTE_ORDER__ == __LITTLE_ENDIAN__ +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +# endif +#elif defined( __BIG_ENDIAN__ ) +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#elif defined( __LITTLE_ENDIAN__ ) +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#endif + +/* if the platform byte order could not be determined, then try to */ +/* set this define using common machine defines */ +#if !defined(PLATFORM_BYTE_ORDER) + +#if defined( __alpha__ ) || defined( __alpha ) || defined( i386 ) || \ + defined( __i386__ ) || defined( _M_I86 ) || defined( _M_IX86 ) || \ + defined( __OS2__ ) || defined( sun386 ) || defined( __TURBOC__ ) || \ + defined( vax ) || defined( vms ) || defined( VMS ) || \ + defined( __VMS ) || defined( _M_X64 ) +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN + +#elif defined( AMIGA ) || defined( applec ) || defined( __AS400__ ) || \ + defined( _CRAY ) || defined( __hppa ) || defined( __hp9000 ) || \ + defined( ibm370 ) || defined( mc68000 ) || defined( m68k ) || \ + defined( __MRC__ ) || defined( __MVS__ ) || defined( __MWERKS__ ) || \ + defined( sparc ) || defined( __sparc) || defined( SYMANTEC_C ) || \ + defined( __VOS__ ) || defined( __TIGCC__ ) || defined( __TANDEM ) || \ + defined( THINK_C ) || defined( __VMCMS__ ) || defined( _AIX ) +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN + +#elif 0 /* **** EDIT HERE IF NECESSARY **** */ +# define PLATFORM_BYTE_ORDER IS_LITTLE_ENDIAN +#elif 0 /* **** EDIT HERE IF NECESSARY **** */ +# define PLATFORM_BYTE_ORDER IS_BIG_ENDIAN +#else +# error Please edit lines 126 or 128 in brg_endian.h to set the platform byte order +#endif + +#endif + +#endif diff --git a/src/java/KP2AKdbLibrary/jni/aes/brg_types.h b/src/java/KP2AKdbLibrary/jni/aes/brg_types.h new file mode 100644 index 00000000..5b199fcb --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/brg_types.h @@ -0,0 +1,226 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 20/12/2007 + + The unsigned integer types defined here are of the form uint_t where + is the length of the type; for example, the unsigned 32-bit type is + 'uint_32t'. These are NOT the same as the 'C99 integer types' that are + defined in the inttypes.h and stdint.h headers since attempts to use these + types have shown that support for them is still highly variable. However, + since the latter are of the form uint_t, a regular expression search + and replace (in VC++ search on 'uint_{:z}t' and replace with 'uint\1_t') + can be used to convert the types used here to the C99 standard types. +*/ + +#ifndef _BRG_TYPES_H +#define _BRG_TYPES_H + +#if defined(__cplusplus) +extern "C" { +#endif + +#include + +#if defined( _MSC_VER ) && ( _MSC_VER >= 1300 ) +# include +# define ptrint_t intptr_t +#elif defined( __ECOS__ ) +# define intptr_t unsigned int +# define ptrint_t intptr_t +#elif defined( __GNUC__ ) && ( __GNUC__ >= 3 ) +# include +# define ptrint_t intptr_t +#else +# define ptrint_t int +#endif + +#ifndef BRG_UI8 +# define BRG_UI8 +# if UCHAR_MAX == 255u + typedef unsigned char uint_8t; +# else +# error Please define uint_8t as an 8-bit unsigned integer type in brg_types.h +# endif +#endif + +#ifndef BRG_UI16 +# define BRG_UI16 +# if USHRT_MAX == 65535u + typedef unsigned short uint_16t; +# else +# error Please define uint_16t as a 16-bit unsigned short type in brg_types.h +# endif +#endif + +#ifndef BRG_UI32 +# define BRG_UI32 +# if UINT_MAX == 4294967295u +# define li_32(h) 0x##h##u + typedef unsigned int uint_32t; +# elif ULONG_MAX == 4294967295u +# define li_32(h) 0x##h##ul + typedef unsigned long uint_32t; +# elif defined( _CRAY ) +# error This code needs 32-bit data types, which Cray machines do not provide +# else +# error Please define uint_32t as a 32-bit unsigned integer type in brg_types.h +# endif +#endif + +#ifndef BRG_UI64 +# if defined( __BORLANDC__ ) && !defined( __MSDOS__ ) +# define BRG_UI64 +# define li_64(h) 0x##h##ui64 + typedef unsigned __int64 uint_64t; +# elif defined( _MSC_VER ) && ( _MSC_VER < 1300 ) /* 1300 == VC++ 7.0 */ +# define BRG_UI64 +# define li_64(h) 0x##h##ui64 + typedef unsigned __int64 uint_64t; +# elif defined( __sun ) && defined( ULONG_MAX ) && ULONG_MAX == 0xfffffffful +# define BRG_UI64 +# define li_64(h) 0x##h##ull + typedef unsigned long long uint_64t; +# elif defined( __MVS__ ) +# define BRG_UI64 +# define li_64(h) 0x##h##ull + typedef unsigned int long long uint_64t; +# elif defined( UINT_MAX ) && UINT_MAX > 4294967295u +# if UINT_MAX == 18446744073709551615u +# define BRG_UI64 +# define li_64(h) 0x##h##u + typedef unsigned int uint_64t; +# endif +# elif defined( ULONG_MAX ) && ULONG_MAX > 4294967295u +# if ULONG_MAX == 18446744073709551615ul +# define BRG_UI64 +# define li_64(h) 0x##h##ul + typedef unsigned long uint_64t; +# endif +# elif defined( ULLONG_MAX ) && ULLONG_MAX > 4294967295u +# if ULLONG_MAX == 18446744073709551615ull +# define BRG_UI64 +# define li_64(h) 0x##h##ull + typedef unsigned long long uint_64t; +# endif +# elif defined( ULONG_LONG_MAX ) && ULONG_LONG_MAX > 4294967295u +# if ULONG_LONG_MAX == 18446744073709551615ull +# define BRG_UI64 +# define li_64(h) 0x##h##ull + typedef unsigned long long uint_64t; +# endif +# endif +#endif + +#if !defined( BRG_UI64 ) +# if defined( NEED_UINT_64T ) +# error Please define uint_64t as an unsigned 64 bit type in brg_types.h +# endif +#endif + +#ifndef RETURN_VALUES +# define RETURN_VALUES +# if defined( DLL_EXPORT ) +# if defined( _MSC_VER ) || defined ( __INTEL_COMPILER ) +# define VOID_RETURN __declspec( dllexport ) void __stdcall +# define INT_RETURN __declspec( dllexport ) int __stdcall +# elif defined( __GNUC__ ) +# define VOID_RETURN __declspec( __dllexport__ ) void +# define INT_RETURN __declspec( __dllexport__ ) int +# else +# error Use of the DLL is only available on the Microsoft, Intel and GCC compilers +# endif +# elif defined( DLL_IMPORT ) +# if defined( _MSC_VER ) || defined ( __INTEL_COMPILER ) +# define VOID_RETURN __declspec( dllimport ) void __stdcall +# define INT_RETURN __declspec( dllimport ) int __stdcall +# elif defined( __GNUC__ ) +# define VOID_RETURN __declspec( __dllimport__ ) void +# define INT_RETURN __declspec( __dllimport__ ) int +# else +# error Use of the DLL is only available on the Microsoft, Intel and GCC compilers +# endif +# elif defined( __WATCOMC__ ) +# define VOID_RETURN void __cdecl +# define INT_RETURN int __cdecl +# else +# define VOID_RETURN void +# define INT_RETURN int +# endif +#endif + +/* These defines are used to detect and set the memory alignment of pointers. + Note that offsets are in bytes. + + ALIGN_OFFSET(x,n) return the positive or zero offset of + the memory addressed by the pointer 'x' + from an address that is aligned on an + 'n' byte boundary ('n' is a power of 2) + + ALIGN_FLOOR(x,n) return a pointer that points to memory + that is aligned on an 'n' byte boundary + and is not higher than the memory address + pointed to by 'x' ('n' is a power of 2) + + ALIGN_CEIL(x,n) return a pointer that points to memory + that is aligned on an 'n' byte boundary + and is not lower than the memory address + pointed to by 'x' ('n' is a power of 2) +*/ + +#define ALIGN_OFFSET(x,n) (((ptrint_t)(x)) & ((n) - 1)) +#define ALIGN_FLOOR(x,n) ((uint_8t*)(x) - ( ((ptrint_t)(x)) & ((n) - 1))) +#define ALIGN_CEIL(x,n) ((uint_8t*)(x) + (-((ptrint_t)(x)) & ((n) - 1))) + +/* These defines are used to declare buffers in a way that allows + faster operations on longer variables to be used. In all these + defines 'size' must be a power of 2 and >= 8. NOTE that the + buffer size is in bytes but the type length is in bits + + UNIT_TYPEDEF(x,size) declares a variable 'x' of length + 'size' bits + + BUFR_TYPEDEF(x,size,bsize) declares a buffer 'x' of length 'bsize' + bytes defined as an array of variables + each of 'size' bits (bsize must be a + multiple of size / 8) + + UNIT_CAST(x,size) casts a variable to a type of + length 'size' bits + + UPTR_CAST(x,size) casts a pointer to a pointer to a + varaiable of length 'size' bits +*/ + +#define UI_TYPE(size) uint_##size##t +#define UNIT_TYPEDEF(x,size) typedef UI_TYPE(size) x +#define BUFR_TYPEDEF(x,size,bsize) typedef UI_TYPE(size) x[bsize / (size >> 3)] +#define UNIT_CAST(x,size) ((UI_TYPE(size) )(x)) +#define UPTR_CAST(x,size) ((UI_TYPE(size)*)(x)) + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/src/java/KP2AKdbLibrary/jni/aes/rfc3686.c b/src/java/KP2AKdbLibrary/jni/aes/rfc3686.c new file mode 100644 index 00000000..b1ca20b0 --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/rfc3686.c @@ -0,0 +1,331 @@ + +#include +#include +#include "aes.h" + +typedef struct +{ unsigned int k_len; + unsigned int m_len; + unsigned char key[32]; + unsigned char iv[8]; + unsigned char nonce[8]; + unsigned char p_txt[36]; + unsigned char c_str[48]; + unsigned char k_str[48]; + unsigned char c_txt[36]; +} test_str; + +test_str tests[] = +{ + { 16, 16, /* Vector 1 */ + { 0xae, 0x68, 0x52, 0xf8, 0x12, 0x10, 0x67, 0xcc, + 0x4b, 0xf7, 0xa5, 0x76, 0x55, 0x77, 0xf3, 0x9e + }, + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }, + { 0x00, 0x00, 0x00, 0x30 + }, + /* "Single block msg" */ + { 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x6d, 0x73, 0x67 + }, + { 0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + }, + { 0xb7, 0x60, 0x33, 0x28, 0xdb, 0xc2, 0x93, 0x1b, + 0x41, 0x0e, 0x16, 0xc8, 0x06, 0x7e, 0x62, 0xdf + }, + { 0xe4, 0x09, 0x5d, 0x4f, 0xb7, 0xa7, 0xb3, 0x79, + 0x2d, 0x61, 0x75, 0xa3, 0x26, 0x13, 0x11, 0xb8 + } + }, + { 16, 32, /* Vector 2 */ + { 0x7e, 0x24, 0x06, 0x78, 0x17, 0xfa, 0xe0, 0xd7, + 0x43, 0xd6, 0xce, 0x1f, 0x32, 0x53, 0x91, 0x63 + }, + { 0xc0, 0x54, 0x3b, 0x59, 0xda, 0x48, 0xd9, 0x0b + }, + { 0x00, 0x6c, 0xb6, 0xdb + }, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f + }, + { 0x00, 0x6c, 0xb6, 0xdb, 0xc0, 0x54, 0x3b, 0x59, + 0xda, 0x48, 0xd9, 0x0b, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x6c, 0xb6, 0xdb, 0xc0, 0x54, 0x3b, 0x59, + 0xda, 0x48, 0xd9, 0x0b, 0x00, 0x00, 0x00, 0x02 + }, + { 0x51, 0x05, 0xa3, 0x05, 0x12, 0x8f, 0x74, 0xde, + 0x71, 0x04, 0x4b, 0xe5, 0x82, 0xd7, 0xdd, 0x87, + 0xfb, 0x3f, 0x0c, 0xef, 0x52, 0xcf, 0x41, 0xdf, + 0xe4, 0xff, 0x2a, 0xc4, 0x8d, 0x5c, 0xa0, 0x37 + }, + { 0x51, 0x04, 0xa1, 0x06, 0x16, 0x8a, 0x72, 0xd9, + 0x79, 0x0d, 0x41, 0xee, 0x8e, 0xda, 0xd3, 0x88, + 0xeb, 0x2e, 0x1e, 0xfc, 0x46, 0xda, 0x57, 0xc8, + 0xfc, 0xe6, 0x30, 0xdf, 0x91, 0x41, 0xbe, 0x28 + } + }, + { 16, 36, /* Vector 3 */ + { 0x76, 0x91, 0xbe, 0x03, 0x5e, 0x50, 0x20, 0xa8, + 0xac, 0x6e, 0x61, 0x85, 0x29, 0xf9, 0xa0, 0xdc + }, + { 0x27, 0x77, 0x7f, 0x3f, 0x4a, 0x17, 0x86, 0xf0 + }, + { 0x00, 0xe0, 0x01, 0x7b + }, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23 + }, + { 0x00, 0xe0, 0x01, 0x7b, 0x27, 0x77, 0x7f, 0x3f, + 0x4a, 0x17, 0x86, 0xf0, 0x00, 0x00, 0x00, 0x01, + 0x00, 0xe0, 0x01, 0x7b, 0x27, 0x77, 0x7f, 0x3f, + 0x4a, 0x17, 0x86, 0xf0, 0x00, 0x00, 0x00, 0x02, + 0x00, 0xe0, 0x01, 0x7b, 0x27, 0x77, 0x7f, 0x3f, + 0x4a, 0x17, 0x86, 0xf0, 0x00, 0x00, 0x00, 0x03 + }, + { 0xc1, 0xce, 0x4a, 0xab, 0x9b, 0x2a, 0xfb, 0xde, + 0xc7, 0x4f, 0x58, 0xe2, 0xe3, 0xd6, 0x7c, 0xd8, + 0x55, 0x51, 0xb6, 0x38, 0xca, 0x78, 0x6e, 0x21, + 0xcd, 0x83, 0x46, 0xf1, 0xb2, 0xee, 0x0e, 0x4c, + 0x05, 0x93, 0x25, 0x0c, 0x17, 0x55, 0x36, 0x00, + 0xa6, 0x3d, 0xfe, 0xcf, 0x56, 0x23, 0x87, 0xe9 + }, + { 0xc1, 0xcf, 0x48, 0xa8, 0x9f, 0x2f, 0xfd, 0xd9, + 0xcf, 0x46, 0x52, 0xe9, 0xef, 0xdb, 0x72, 0xd7, + 0x45, 0x40, 0xa4, 0x2b, 0xde, 0x6d, 0x78, 0x36, + 0xd5, 0x9a, 0x5c, 0xea, 0xae, 0xf3, 0x10, 0x53, + 0x25, 0xb2, 0x07, 0x2f + } + }, + { 24, 16, /* Vector 4 */ + { 0x16, 0xaf, 0x5b, 0x14, 0x5f, 0xc9, 0xf5, 0x79, + 0xc1, 0x75, 0xf9, 0x3e, 0x3b, 0xfb, 0x0e, 0xed, + 0x86, 0x3d, 0x06, 0xcc, 0xfd, 0xb7, 0x85, 0x15 + }, + { 0x36, 0x73, 0x3c, 0x14, 0x7d, 0x6d, 0x93, 0xcb + }, + { 0x00, 0x00, 0x00, 0x48 + }, + /* "Single block msg" */ + { 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x6d, 0x73, 0x67 + }, + { 0x00, 0x00, 0x00, 0x48, 0x36, 0x73, 0x3c, 0x14, + 0x7d, 0x6d, 0x93, 0xcb, 0x00, 0x00, 0x00, 0x01 + }, + { 0x18, 0x3c, 0x56, 0x28, 0x8e, 0x3c, 0xe9, 0xaa, + 0x22, 0x16, 0x56, 0xcb, 0x23, 0xa6, 0x9a, 0x4f + }, + { 0x4b, 0x55, 0x38, 0x4f, 0xe2, 0x59, 0xc9, 0xc8, + 0x4e, 0x79, 0x35, 0xa0, 0x03, 0xcb, 0xe9, 0x28 + } + }, + { 24, 32, /* Vector 5 */ + { 0x7c, 0x5c, 0xb2, 0x40, 0x1b, 0x3d, 0xc3, 0x3c, + 0x19, 0xe7, 0x34, 0x08, 0x19, 0xe0, 0xf6, 0x9c, + 0x67, 0x8c, 0x3d, 0xb8, 0xe6, 0xf6, 0xa9, 0x1a + }, + { 0x02, 0x0c, 0x6e, 0xad, 0xc2, 0xcb, 0x50, 0x0d + }, + { 0x00, 0x96, 0xb0, 0x3b + }, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f + }, + { 0x00, 0x96, 0xb0, 0x3b, 0x02, 0x0c, 0x6e, 0xad, + 0xc2, 0xcb, 0x50, 0x0d, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x96, 0xb0, 0x3b, 0x02, 0x0c, 0x6e, 0xad, + 0xc2, 0xcb, 0x50, 0x0d, 0x00, 0x00, 0x00, 0x02 + }, + { 0x45, 0x33, 0x41, 0xff, 0x64, 0x9e, 0x25, 0x35, + 0x76, 0xd6, 0xa0, 0xf1, 0x7d, 0x3c, 0xc3, 0x90, + 0x94, 0x81, 0x62, 0x0f, 0x4e, 0xc1, 0xb1, 0x8b, + 0xe4, 0x06, 0xfa, 0xe4, 0x5e, 0xe9, 0xe5, 0x1f + }, + { 0x45, 0x32, 0x43, 0xfc, 0x60, 0x9b, 0x23, 0x32, + 0x7e, 0xdf, 0xaa, 0xfa, 0x71, 0x31, 0xcd, 0x9f, + 0x84, 0x90, 0x70, 0x1c, 0x5a, 0xd4, 0xa7, 0x9c, + 0xfc, 0x1f, 0xe0, 0xff, 0x42, 0xf4, 0xfb, 0x00 + } + }, + { 24, 36, /* Vector 6 */ + { 0x02, 0xbf, 0x39, 0x1e, 0xe8, 0xec, 0xb1, 0x59, + 0xb9, 0x59, 0x61, 0x7b, 0x09, 0x65, 0x27, 0x9b, + 0xf5, 0x9b, 0x60, 0xa7, 0x86, 0xd3, 0xe0, 0xfe + }, + { 0x5c, 0xbd, 0x60, 0x27, 0x8d, 0xcc, 0x09, 0x12 + }, + { 0x00, 0x07, 0xbd, 0xfd + }, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23 + }, + { 0x00, 0x07, 0xbd, 0xfd, 0x5c, 0xbd, 0x60, 0x27, + 0x8d, 0xcc, 0x09, 0x12, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x07, 0xbd, 0xfd, 0x5c, 0xbd, 0x60, 0x27, + 0x8d, 0xcc, 0x09, 0x12, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x07, 0xbd, 0xfd, 0x5c, 0xbd, 0x60, 0x27, + 0x8d, 0xcc, 0x09, 0x12, 0x00, 0x00, 0x00, 0x02 + }, + { 0x96, 0x88, 0x3d, 0xc6, 0x5a, 0x59, 0x74, 0x28, + 0x5c, 0x02, 0x77, 0xda, 0xd1, 0xfa, 0xe9, 0x57, + 0xc2, 0x99, 0xae, 0x86, 0xd2, 0x84, 0x73, 0x9f, + 0x5d, 0x2f, 0xd2, 0x0a, 0x7a, 0x32, 0x3f, 0x97, + 0x8b, 0xcf, 0x2b, 0x16, 0x39, 0x99, 0xb2, 0x26, + 0x15, 0xb4, 0x9c, 0xd4, 0xfe, 0x57, 0x39, 0x98 + }, + { 0x96, 0x89, 0x3f, 0xc5, 0x5e, 0x5c, 0x72, 0x2f, + 0x54, 0x0b, 0x7d, 0xd1, 0xdd, 0xf7, 0xe7, 0x58, + 0xd2, 0x88, 0xbc, 0x95, 0xc6, 0x91, 0x65, 0x88, + 0x45, 0x36, 0xc8, 0x11, 0x66, 0x2f, 0x21, 0x88, + 0xab, 0xee, 0x09, 0x35 + } + }, + { 32, 16, /* Vector 7 */ + { 0x77, 0x6b, 0xef, 0xf2, 0x85, 0x1d, 0xb0, 0x6f, + 0x4c, 0x8a, 0x05, 0x42, 0xc8, 0x69, 0x6f, 0x6c, + 0x6a, 0x81, 0xaf, 0x1e, 0xec, 0x96, 0xb4, 0xd3, + 0x7f, 0xc1, 0xd6, 0x89, 0xe6, 0xc1, 0xc1, 0x04 + }, + { 0xdb, 0x56, 0x72, 0xc9, 0x7a, 0xa8, 0xf0, 0xb2 + }, + { 0x00, 0x00, 0x00, 0x60 + }, + /* "Single block msg" */ + { 0x53, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x62, + 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x6d, 0x73, 0x67 + }, + { 0x00, 0x00, 0x00, 0x60, 0xdb, 0x56, 0x72, 0xc9, + 0x7a, 0xa8, 0xf0, 0xb2, 0x00, 0x00, 0x00, 0x01 + }, + { 0x47, 0x33, 0xbe, 0x7a, 0xd3, 0xe7, 0x6e, 0xa5, + 0x3a, 0x67, 0x00, 0xb7, 0x51, 0x8e, 0x93, 0xa7 + }, + { 0x14, 0x5a, 0xd0, 0x1d, 0xbf, 0x82, 0x4e, 0xc7, + 0x56, 0x08, 0x63, 0xdc, 0x71, 0xe3, 0xe0, 0xc0 + } + }, + { 32, 32, /* Vector 8 */ + { 0xf6, 0xd6, 0x6d, 0x6b, 0xd5, 0x2d, 0x59, 0xbb, + 0x07, 0x96, 0x36, 0x58, 0x79, 0xef, 0xf8, 0x86, + 0xc6, 0x6d, 0xd5, 0x1a, 0x5b, 0x6a, 0x99, 0x74, + 0x4b, 0x50, 0x59, 0x0c, 0x87, 0xa2, 0x38, 0x84 + }, + { 0xc1, 0x58, 0x5e, 0xf1, 0x5a, 0x43, 0xd8, 0x75 + }, + { 0x00, 0xfa, 0xac, 0x24 + }, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f + }, + { 0x00, 0xfa, 0xac, 0x24, 0xc1, 0x58, 0x5e, 0xf1, + 0x5a, 0x43, 0xd8, 0x75, 0x00, 0x00, 0x00, 0x01, + 0x00, 0xfa, 0xac, 0x24, 0xc1, 0x58, 0x5e, 0xf1, + 0x5a, 0x43, 0xd8, 0x75, 0x00, 0x00, 0x00, 0x02 + }, + { 0xf0, 0x5f, 0x21, 0x18, 0x3c, 0x91, 0x67, 0x2b, + 0x41, 0xe7, 0x0a, 0x00, 0x8c, 0x43, 0xbc, 0xa6, + 0xa8, 0x21, 0x79, 0x43, 0x9b, 0x96, 0x8b, 0x7d, + 0x4d, 0x29, 0x99, 0x06, 0x8f, 0x59, 0xb1, 0x03 + }, + { 0xf0, 0x5e, 0x23, 0x1b, 0x38, 0x94, 0x61, 0x2c, + 0x49, 0xee, 0x00, 0x0b, 0x80, 0x4e, 0xb2, 0xa9, + 0xb8, 0x30, 0x6b, 0x50, 0x8f, 0x83, 0x9d, 0x6a, + 0x55, 0x30, 0x83, 0x1d, 0x93, 0x44, 0xaf, 0x1c + } + }, + { 32, 36, /* Vector 9 */ + { 0xff, 0x7a, 0x61, 0x7c, 0xe6, 0x91, 0x48, 0xe4, + 0xf1, 0x72, 0x6e, 0x2f, 0x43, 0x58, 0x1d, 0xe2, + 0xaa, 0x62, 0xd9, 0xf8, 0x05, 0x53, 0x2e, 0xdf, + 0xf1, 0xee, 0xd6, 0x87, 0xfb, 0x54, 0x15, 0x3d + }, + { 0x51, 0xa5, 0x1d, 0x70, 0xa1, 0xc1, 0x11, 0x48 + }, + { 0x00, 0x1c, 0xc5, 0xb7 + }, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, + 0x20, 0x21, 0x22, 0x23 + }, + { 0x00, 0x1c, 0xc5, 0xb7, 0x51, 0xa5, 0x1d, 0x70, + 0xa1, 0xc1, 0x11, 0x48, 0x00, 0x00, 0x00, 0x01, + 0x00, 0x1c, 0xc5, 0xb7, 0x51, 0xa5, 0x1d, 0x70, + 0xa1, 0xc1, 0x11, 0x48, 0x00, 0x00, 0x00, 0x02, + 0x00, 0x1c, 0xc5, 0xb7, 0x51, 0xa5, 0x1d, 0x70, + 0xa1, 0xc1, 0x11, 0x48, 0x00, 0x00, 0x00, 0x03 + }, + { 0xeb, 0x6d, 0x50, 0x81, 0x19, 0x0e, 0xbd, 0xf0, + 0xc6, 0x7c, 0x9e, 0x4d, 0x26, 0xc7, 0x41, 0xa5, + 0xa4, 0x16, 0xcd, 0x95, 0x71, 0x7c, 0xeb, 0x10, + 0xec, 0x95, 0xda, 0xae, 0x9f, 0xcb, 0x19, 0x00, + 0x3e, 0xe1, 0xc4, 0x9b, 0xc6, 0xb9, 0xca, 0x21, + 0x3f, 0x6e, 0xe2, 0x71, 0xd0, 0xa9, 0x33, 0x39 + }, + { 0xeb, 0x6c, 0x52, 0x82, 0x1d, 0x0b, 0xbb, 0xf7, + 0xce, 0x75, 0x94, 0x46, 0x2a, 0xca, 0x4f, 0xaa, + 0xb4, 0x07, 0xdf, 0x86, 0x65, 0x69, 0xfd, 0x07, + 0xf4, 0x8c, 0xc0, 0xb5, 0x83, 0xd6, 0x07, 0x1f, + 0x1e, 0xc0, 0xe6, 0xb8 + } + } +}; + +void rfc3686_inc(unsigned char ctr_buf[AES_BLOCK_SIZE]) +{ + if(!(++(ctr_buf[15]))) + if(!(++(ctr_buf[14]))) + if(!(++(ctr_buf[13]))) + ++(ctr_buf[12]); +} + +void rfc3686_init( unsigned char nonce[4], unsigned char iv[8], unsigned char ctr_buf[AES_BLOCK_SIZE]) +{ + memcpy(ctr_buf, nonce, 4); + memcpy(ctr_buf + 4, iv, 8); + memset(ctr_buf + 12, 0, 4); + rfc3686_inc(ctr_buf); +} + +AES_RETURN rfc3686_crypt(const unsigned char *ibuf, unsigned char *obuf, int len, + unsigned char *cbuf, aes_encrypt_ctx cx[1]) +{ + return aes_ctr_crypt(ibuf, obuf, len, cbuf, rfc3686_inc, cx); +} + +void rfc3686_test(void) +{ aes_encrypt_ctx aes_ctx[1]; + unsigned char ctr_buf[AES_BLOCK_SIZE]; + unsigned char obuf[36]; + unsigned int i; + + for( i = 0 ; i < sizeof(tests) / sizeof(test_str) ; ++i ) + { + aes_encrypt_key(tests[i].key, tests[i].k_len, aes_ctx); + rfc3686_init(tests[i].nonce, tests[i].iv, ctr_buf); + rfc3686_crypt(tests[i].p_txt, obuf, tests[i].m_len, ctr_buf, aes_ctx); + if(memcmp(obuf, tests[i].c_txt, tests[i].m_len) != 0) + printf("\nerror"); + } +} + +int main(void) +{ + rfc3686_test(); + return 0; +} \ No newline at end of file diff --git a/src/java/KP2AKdbLibrary/jni/aes/tablegen.c b/src/java/KP2AKdbLibrary/jni/aes/tablegen.c new file mode 100644 index 00000000..78719abb --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/tablegen.c @@ -0,0 +1,319 @@ +/* + --------------------------------------------------------------------------- + Copyright (c) 1998-2008, Brian Gladman, Worcester, UK. All rights reserved. + + LICENSE TERMS + + The redistribution and use of this software (with or without changes) + is allowed without the payment of fees or royalties provided that: + + 1. source code distributions include the above copyright notice, this + list of conditions and the following disclaimer; + + 2. binary distributions include the above copyright notice, this list + of conditions and the following disclaimer in their documentation; + + 3. the name of the copyright holder is not used to endorse products + built using this software without specific written permission. + + DISCLAIMER + + This software is provided 'as is' with no explicit or implied warranties + in respect of its properties, including, but not limited to, correctness + and/or fitness for purpose. + --------------------------------------------------------------------------- + Issue Date: 20/12/2007 +*/ + +#define DO_TABLES + +#include +#include "aesopt.h" + +#define sb_data(w) {\ + w(0x63), w(0x7c), w(0x77), w(0x7b), w(0xf2), w(0x6b), w(0x6f), w(0xc5),\ + w(0x30), w(0x01), w(0x67), w(0x2b), w(0xfe), w(0xd7), w(0xab), w(0x76),\ + w(0xca), w(0x82), w(0xc9), w(0x7d), w(0xfa), w(0x59), w(0x47), w(0xf0),\ + w(0xad), w(0xd4), w(0xa2), w(0xaf), w(0x9c), w(0xa4), w(0x72), w(0xc0),\ + w(0xb7), w(0xfd), w(0x93), w(0x26), w(0x36), w(0x3f), w(0xf7), w(0xcc),\ + w(0x34), w(0xa5), w(0xe5), w(0xf1), w(0x71), w(0xd8), w(0x31), w(0x15),\ + w(0x04), w(0xc7), w(0x23), w(0xc3), w(0x18), w(0x96), w(0x05), w(0x9a),\ + w(0x07), w(0x12), w(0x80), w(0xe2), w(0xeb), w(0x27), w(0xb2), w(0x75),\ + w(0x09), w(0x83), w(0x2c), w(0x1a), w(0x1b), w(0x6e), w(0x5a), w(0xa0),\ + w(0x52), w(0x3b), w(0xd6), w(0xb3), w(0x29), w(0xe3), w(0x2f), w(0x84),\ + w(0x53), w(0xd1), w(0x00), w(0xed), w(0x20), w(0xfc), w(0xb1), w(0x5b),\ + w(0x6a), w(0xcb), w(0xbe), w(0x39), w(0x4a), w(0x4c), w(0x58), w(0xcf),\ + w(0xd0), w(0xef), w(0xaa), w(0xfb), w(0x43), w(0x4d), w(0x33), w(0x85),\ + w(0x45), w(0xf9), w(0x02), w(0x7f), w(0x50), w(0x3c), w(0x9f), w(0xa8),\ + w(0x51), w(0xa3), w(0x40), w(0x8f), w(0x92), w(0x9d), w(0x38), w(0xf5),\ + w(0xbc), w(0xb6), w(0xda), w(0x21), w(0x10), w(0xff), w(0xf3), w(0xd2),\ + w(0xcd), w(0x0c), w(0x13), w(0xec), w(0x5f), w(0x97), w(0x44), w(0x17),\ + w(0xc4), w(0xa7), w(0x7e), w(0x3d), w(0x64), w(0x5d), w(0x19), w(0x73),\ + w(0x60), w(0x81), w(0x4f), w(0xdc), w(0x22), w(0x2a), w(0x90), w(0x88),\ + w(0x46), w(0xee), w(0xb8), w(0x14), w(0xde), w(0x5e), w(0x0b), w(0xdb),\ + w(0xe0), w(0x32), w(0x3a), w(0x0a), w(0x49), w(0x06), w(0x24), w(0x5c),\ + w(0xc2), w(0xd3), w(0xac), w(0x62), w(0x91), w(0x95), w(0xe4), w(0x79),\ + w(0xe7), w(0xc8), w(0x37), w(0x6d), w(0x8d), w(0xd5), w(0x4e), w(0xa9),\ + w(0x6c), w(0x56), w(0xf4), w(0xea), w(0x65), w(0x7a), w(0xae), w(0x08),\ + w(0xba), w(0x78), w(0x25), w(0x2e), w(0x1c), w(0xa6), w(0xb4), w(0xc6),\ + w(0xe8), w(0xdd), w(0x74), w(0x1f), w(0x4b), w(0xbd), w(0x8b), w(0x8a),\ + w(0x70), w(0x3e), w(0xb5), w(0x66), w(0x48), w(0x03), w(0xf6), w(0x0e),\ + w(0x61), w(0x35), w(0x57), w(0xb9), w(0x86), w(0xc1), w(0x1d), w(0x9e),\ + w(0xe1), w(0xf8), w(0x98), w(0x11), w(0x69), w(0xd9), w(0x8e), w(0x94),\ + w(0x9b), w(0x1e), w(0x87), w(0xe9), w(0xce), w(0x55), w(0x28), w(0xdf),\ + w(0x8c), w(0xa1), w(0x89), w(0x0d), w(0xbf), w(0xe6), w(0x42), w(0x68),\ + w(0x41), w(0x99), w(0x2d), w(0x0f), w(0xb0), w(0x54), w(0xbb), w(0x16) } + +#define isb_data(w) {\ + w(0x52), w(0x09), w(0x6a), w(0xd5), w(0x30), w(0x36), w(0xa5), w(0x38),\ + w(0xbf), w(0x40), w(0xa3), w(0x9e), w(0x81), w(0xf3), w(0xd7), w(0xfb),\ + w(0x7c), w(0xe3), w(0x39), w(0x82), w(0x9b), w(0x2f), w(0xff), w(0x87),\ + w(0x34), w(0x8e), w(0x43), w(0x44), w(0xc4), w(0xde), w(0xe9), w(0xcb),\ + w(0x54), w(0x7b), w(0x94), w(0x32), w(0xa6), w(0xc2), w(0x23), w(0x3d),\ + w(0xee), w(0x4c), w(0x95), w(0x0b), w(0x42), w(0xfa), w(0xc3), w(0x4e),\ + w(0x08), w(0x2e), w(0xa1), w(0x66), w(0x28), w(0xd9), w(0x24), w(0xb2),\ + w(0x76), w(0x5b), w(0xa2), w(0x49), w(0x6d), w(0x8b), w(0xd1), w(0x25),\ + w(0x72), w(0xf8), w(0xf6), w(0x64), w(0x86), w(0x68), w(0x98), w(0x16),\ + w(0xd4), w(0xa4), w(0x5c), w(0xcc), w(0x5d), w(0x65), w(0xb6), w(0x92),\ + w(0x6c), w(0x70), w(0x48), w(0x50), w(0xfd), w(0xed), w(0xb9), w(0xda),\ + w(0x5e), w(0x15), w(0x46), w(0x57), w(0xa7), w(0x8d), w(0x9d), w(0x84),\ + w(0x90), w(0xd8), w(0xab), w(0x00), w(0x8c), w(0xbc), w(0xd3), w(0x0a),\ + w(0xf7), w(0xe4), w(0x58), w(0x05), w(0xb8), w(0xb3), w(0x45), w(0x06),\ + w(0xd0), w(0x2c), w(0x1e), w(0x8f), w(0xca), w(0x3f), w(0x0f), w(0x02),\ + w(0xc1), w(0xaf), w(0xbd), w(0x03), w(0x01), w(0x13), w(0x8a), w(0x6b),\ + w(0x3a), w(0x91), w(0x11), w(0x41), w(0x4f), w(0x67), w(0xdc), w(0xea),\ + w(0x97), w(0xf2), w(0xcf), w(0xce), w(0xf0), w(0xb4), w(0xe6), w(0x73),\ + w(0x96), w(0xac), w(0x74), w(0x22), w(0xe7), w(0xad), w(0x35), w(0x85),\ + w(0xe2), w(0xf9), w(0x37), w(0xe8), w(0x1c), w(0x75), w(0xdf), w(0x6e),\ + w(0x47), w(0xf1), w(0x1a), w(0x71), w(0x1d), w(0x29), w(0xc5), w(0x89),\ + w(0x6f), w(0xb7), w(0x62), w(0x0e), w(0xaa), w(0x18), w(0xbe), w(0x1b),\ + w(0xfc), w(0x56), w(0x3e), w(0x4b), w(0xc6), w(0xd2), w(0x79), w(0x20),\ + w(0x9a), w(0xdb), w(0xc0), w(0xfe), w(0x78), w(0xcd), w(0x5a), w(0xf4),\ + w(0x1f), w(0xdd), w(0xa8), w(0x33), w(0x88), w(0x07), w(0xc7), w(0x31),\ + w(0xb1), w(0x12), w(0x10), w(0x59), w(0x27), w(0x80), w(0xec), w(0x5f),\ + w(0x60), w(0x51), w(0x7f), w(0xa9), w(0x19), w(0xb5), w(0x4a), w(0x0d),\ + w(0x2d), w(0xe5), w(0x7a), w(0x9f), w(0x93), w(0xc9), w(0x9c), w(0xef),\ + w(0xa0), w(0xe0), w(0x3b), w(0x4d), w(0xae), w(0x2a), w(0xf5), w(0xb0),\ + w(0xc8), w(0xeb), w(0xbb), w(0x3c), w(0x83), w(0x53), w(0x99), w(0x61),\ + w(0x17), w(0x2b), w(0x04), w(0x7e), w(0xba), w(0x77), w(0xd6), w(0x26),\ + w(0xe1), w(0x69), w(0x14), w(0x63), w(0x55), w(0x21), w(0x0c), w(0x7d) } + +#define mm_data(w) {\ + w(0x00), w(0x01), w(0x02), w(0x03), w(0x04), w(0x05), w(0x06), w(0x07),\ + w(0x08), w(0x09), w(0x0a), w(0x0b), w(0x0c), w(0x0d), w(0x0e), w(0x0f),\ + w(0x10), w(0x11), w(0x12), w(0x13), w(0x14), w(0x15), w(0x16), w(0x17),\ + w(0x18), w(0x19), w(0x1a), w(0x1b), w(0x1c), w(0x1d), w(0x1e), w(0x1f),\ + w(0x20), w(0x21), w(0x22), w(0x23), w(0x24), w(0x25), w(0x26), w(0x27),\ + w(0x28), w(0x29), w(0x2a), w(0x2b), w(0x2c), w(0x2d), w(0x2e), w(0x2f),\ + w(0x30), w(0x31), w(0x32), w(0x33), w(0x34), w(0x35), w(0x36), w(0x37),\ + w(0x38), w(0x39), w(0x3a), w(0x3b), w(0x3c), w(0x3d), w(0x3e), w(0x3f),\ + w(0x40), w(0x41), w(0x42), w(0x43), w(0x44), w(0x45), w(0x46), w(0x47),\ + w(0x48), w(0x49), w(0x4a), w(0x4b), w(0x4c), w(0x4d), w(0x4e), w(0x4f),\ + w(0x50), w(0x51), w(0x52), w(0x53), w(0x54), w(0x55), w(0x56), w(0x57),\ + w(0x58), w(0x59), w(0x5a), w(0x5b), w(0x5c), w(0x5d), w(0x5e), w(0x5f),\ + w(0x60), w(0x61), w(0x62), w(0x63), w(0x64), w(0x65), w(0x66), w(0x67),\ + w(0x68), w(0x69), w(0x6a), w(0x6b), w(0x6c), w(0x6d), w(0x6e), w(0x6f),\ + w(0x70), w(0x71), w(0x72), w(0x73), w(0x74), w(0x75), w(0x76), w(0x77),\ + w(0x78), w(0x79), w(0x7a), w(0x7b), w(0x7c), w(0x7d), w(0x7e), w(0x7f),\ + w(0x80), w(0x81), w(0x82), w(0x83), w(0x84), w(0x85), w(0x86), w(0x87),\ + w(0x88), w(0x89), w(0x8a), w(0x8b), w(0x8c), w(0x8d), w(0x8e), w(0x8f),\ + w(0x90), w(0x91), w(0x92), w(0x93), w(0x94), w(0x95), w(0x96), w(0x97),\ + w(0x98), w(0x99), w(0x9a), w(0x9b), w(0x9c), w(0x9d), w(0x9e), w(0x9f),\ + w(0xa0), w(0xa1), w(0xa2), w(0xa3), w(0xa4), w(0xa5), w(0xa6), w(0xa7),\ + w(0xa8), w(0xa9), w(0xaa), w(0xab), w(0xac), w(0xad), w(0xae), w(0xaf),\ + w(0xb0), w(0xb1), w(0xb2), w(0xb3), w(0xb4), w(0xb5), w(0xb6), w(0xb7),\ + w(0xb8), w(0xb9), w(0xba), w(0xbb), w(0xbc), w(0xbd), w(0xbe), w(0xbf),\ + w(0xc0), w(0xc1), w(0xc2), w(0xc3), w(0xc4), w(0xc5), w(0xc6), w(0xc7),\ + w(0xc8), w(0xc9), w(0xca), w(0xcb), w(0xcc), w(0xcd), w(0xce), w(0xcf),\ + w(0xd0), w(0xd1), w(0xd2), w(0xd3), w(0xd4), w(0xd5), w(0xd6), w(0xd7),\ + w(0xd8), w(0xd9), w(0xda), w(0xdb), w(0xdc), w(0xdd), w(0xde), w(0xdf),\ + w(0xe0), w(0xe1), w(0xe2), w(0xe3), w(0xe4), w(0xe5), w(0xe6), w(0xe7),\ + w(0xe8), w(0xe9), w(0xea), w(0xeb), w(0xec), w(0xed), w(0xee), w(0xef),\ + w(0xf0), w(0xf1), w(0xf2), w(0xf3), w(0xf4), w(0xf5), w(0xf6), w(0xf7),\ + w(0xf8), w(0xf9), w(0xfa), w(0xfb), w(0xfc), w(0xfd), w(0xfe), w(0xff) } + +#define rc_data(w) {\ + w(0x01), w(0x02), w(0x04), w(0x08), w(0x10),w(0x20), w(0x40), w(0x80),\ + w(0x1b), w(0x36) } + +#define h0(x) (x) + +#define w0(p) bytes2word(p, 0, 0, 0) +#define w1(p) bytes2word(0, p, 0, 0) +#define w2(p) bytes2word(0, 0, p, 0) +#define w3(p) bytes2word(0, 0, 0, p) + +#define u0(p) bytes2word(f2(p), p, p, f3(p)) +#define u1(p) bytes2word(f3(p), f2(p), p, p) +#define u2(p) bytes2word(p, f3(p), f2(p), p) +#define u3(p) bytes2word(p, p, f3(p), f2(p)) + +#define v0(p) bytes2word(fe(p), f9(p), fd(p), fb(p)) +#define v1(p) bytes2word(fb(p), fe(p), f9(p), fd(p)) +#define v2(p) bytes2word(fd(p), fb(p), fe(p), f9(p)) +#define v3(p) bytes2word(f9(p), fd(p), fb(p), fe(p)) + +#define f2(x) ((x<<1) ^ (((x>>7) & 1) * WPOLY)) +#define f4(x) ((x<<2) ^ (((x>>6) & 1) * WPOLY) ^ (((x>>6) & 2) * WPOLY)) +#define f8(x) ((x<<3) ^ (((x>>5) & 1) * WPOLY) ^ (((x>>5) & 2) * WPOLY) \ + ^ (((x>>5) & 4) * WPOLY)) +#define f3(x) (f2(x) ^ x) +#define f9(x) (f8(x) ^ x) +#define fb(x) (f8(x) ^ f2(x) ^ x) +#define fd(x) (f8(x) ^ f4(x) ^ x) +#define fe(x) (f8(x) ^ f4(x) ^ f2(x)) + +#include "aestab.h" + +#define t_parm(m,n) "t_"#m#n, t_##m##n + +void rtab(FILE *f, unsigned char *h, const unsigned int t[RC_LENGTH]) +{ int i; + + fprintf(f, "\nuint_32t %s[RC_LENGTH] = \n{", h); + + for(i = 0; i < RC_LENGTH; ++i) + { + if(i % 4 == 0) + fprintf(f, "\n "); + if(i != RC_LENGTH - 1) + fprintf(f, "0x%08x, ", t[i]); + else + fprintf(f, "0x%08x ", t[i]); + } + + fprintf(f, "\n};\n"); +} + +void btab_1(FILE *f, unsigned char *h, const unsigned char t[256]) +{ int i; + + fprintf(f, "\nuint_8t %s[256] = \n{", h); + + for(i = 0; i < 256; ++i) + { + if(i % 8 == 0) + fprintf(f, "\n "); + if(i != 255) + fprintf(f, "0x%02x, ", t[i]); + else + fprintf(f, "0x%02x ", t[i]); + } + + fprintf(f, "\n};\n"); +} + +void wtab_1(FILE *f, unsigned char *h, const unsigned int t[256]) +{ int i; + + fprintf(f, "\nuint_32t %s[256] = \n{", h); + + for(i = 0; i < 256; ++i) + { + if(i % 4 == 0) + fprintf(f, "\n "); + if(i != 255) + fprintf(f, "0x%08x, ", t[i]); + else + fprintf(f, "0x%08x ", t[i]); + } + + fprintf(f, "\n};\n"); +} + +void wtab_4(FILE *f, unsigned char *h, const unsigned int t[4][256]) +{ int i, j; + + fprintf(f, "\nuint_32t %s[4][256] = \n{", h); + + for(i = 0; i < 4; ++i) + { + fprintf(f, "\n {"); + + for(j = 0; j < 256; ++j) + { + if(j % 4 == 0) + fprintf(f, "\n "); + if(j != 255) + fprintf(f, "0x%08x, ", t[i][j]); + else + fprintf(f, "0x%08x ", t[i][j]); + } + + if(i != 3) + fprintf(f, "\n },"); + else + fprintf(f, "\n }"); + } + + fprintf(f, "\n};\n"); +} + +int main(void) +{ FILE *f; + + f = fopen("aestab2.c", "w"); + + fprintf(f, "\n#include \"aes.h\"\n"); + fprintf(f, "\n#define RC_LENGTH (5 * (AES_BLOCK_SIZE / 4 - 2))\n"); + fprintf(f, "\nvoid aes_init() \n{ \n}\n"); + + rtab(f, t_parm(r,c)); + +#if defined( SBX_SET ) + btab_1(f, t_parm(s,box)); +#endif + +#if defined( ISB_SET ) + btab_1(f, t_parm(i,box)); +#endif + +#if defined( FT1_SET ) + wtab_1(f, t_parm(f,n)); +#endif +#if defined( FT4_SET ) + wtab_4(f, t_parm(f,n)); +#endif + +#if defined( FL1_SET ) + wtab_1(f, t_parm(f,l)); +#endif +#if defined( FL4_SET ) + wtab_4(f, t_parm(f,l)); +#endif + +#if defined( IT1_SET ) + wtab_1(f, t_parm(i,n)); +#endif +#if defined( IT4_SET ) + wtab_4(f, t_parm(i,n)); +#endif + +#if defined( IL1_SET ) + wtab_1(f, t_parm(i,l)); +#endif +#if defined( IL4_SET ) + wtab_4(f, t_parm(i,l)); +#endif + +#if defined( LS1_SET ) +#if !defined( FL1_SET ) + wtab_1(f, t_parm(l,s)); +#endif +#endif +#if defined( LS4_SET ) +#if !defined( FL4_SET ) + wtab_4(f, t_parm(l,s)); +#endif +#endif + +#if defined( IM1_SET ) + wtab_1(f, t_parm(i,m)); +#endif +#if defined( IM4_SET ) + wtab_4(f, t_parm(i,m)); +#endif + + fclose(f); + return 0; +} diff --git a/src/java/KP2AKdbLibrary/jni/aes/vb.txt b/src/java/KP2AKdbLibrary/jni/aes/vb.txt new file mode 100644 index 00000000..c5a365b4 --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/vb.txt @@ -0,0 +1,263 @@ + +Private Const BlockLength = 16 ' maximum block length in bytes +Private Const BlockLengthMax = 32 ' maximum block length in bytes +Private Const KeyLengthMax = 32 ' maximum block length in bytes +Private Const KeyScheduleLengthMax = 64 ' maximum key schedule length in bytes + +Private Type EncCtx ' type to hold the AES encryption context data + Ekey(0 To KeyScheduleLengthMax - 1) As Long +End Type + +Private Type DecCtx ' type to hold the AES decryption context data + Ekey(0 To KeyScheduleLengthMax - 1) As Long +End Type + +Private Type Key ' type to hold user key data + K(0 To KeyLengthMax - 1) As Byte +End Type + +Private Type InOut ' type to hold cipher input and output blocks + IO(0 To BlockLength - 1) As Byte +End Type + +Private Type BigInOut ' type to hold cipher input and output blocks + IO(0 To 128 * BlockLength - 1) As Byte +End Type + +Rem Change "c:\temp\" in the following lines to the directory path where the AES DLL is located +Private Declare Function AesEncryptKey128 Lib "c:\temp\aes.dll" _ + Alias "_aes_encrypt_key128@8" (K As Key, C As EncCtx) As Integer +Private Declare Function AesEncryptKey192 Lib "c:\temp\aes.dll" _ + Alias "_aes_encrypt_key192@8" (K As Key, C As EncCtx) As Integer +Private Declare Function AesEncryptKey256 Lib "c:\temp\aes.dll" _ + Alias "_aes_encrypt_key256@8" (K As Key, C As EncCtx) As Integer +Private Declare Function AesEncryptKey Lib "c:\temp\aes.dll" _ + Alias "_aes_encrypt_key@12" (K As Key, ByVal N As Integer, C As EncCtx) As Integer +Private Declare Function AesEncrypt Lib "c:\temp\aes.dll" _ + Alias "_aes_encrypt@12" (Ib As InOut, Ob As InOut, C As EncCtx) As Integer +Private Declare Function AesDecryptKey128 Lib "c:\temp\aes.dll" _ + Alias "_aes_decrypt_key128@8" (K As Key, C As DecCtx) As Integer +Private Declare Function AesDecryptKey192 Lib "c:\temp\aes.dll" _ + Alias "_aes_decrypt_key192@8" (K As Key, C As DecCtx) As Integer +Private Declare Function AesDecryptKey256 Lib "c:\temp\aes.dll" _ + Alias "_aes_decrypt_key256@8" (K As Key, C As DecCtx) As Integer +Private Declare Function AesDecryptKey Lib "c:\temp\aes.dll" _ + Alias "_aes_decrypt_key@12" (K As Key, ByVal N As Long, C As DecCtx) As Integer +Private Declare Function AesDecrypt Lib "c:\temp\aes.dll" _ + Alias "_aes_decrypt@12" (Ib As InOut, Ob As InOut, C As DecCtx) As Integer + +Private Declare Function AesModeReset Lib "c:\temp\aes.dll" Alias "_aes_mode_reset@4" _ + (C As EncCtx) As Integer +Private Declare Function AesEcbEncrypt Lib "c:\temp\aes.dll" Alias "_aes_ecb_encrypt@16" _ + (Ib As BigInOut, Ob As BigInOut, ByVal N As Long, C As EncCtx) As Integer +Private Declare Function AesEcbDecrypt Lib "c:\temp\aes.dll" Alias "_aes_ecb_decrypt@16" _ + (Ib As BigInOut, Ob As BigInOut, ByVal N As Long, C As DecCtx) As Integer +Private Declare Function AesCbcEncrypt Lib "c:\temp\aes.dll" Alias "_aes_cbc_encrypt@20" _ + (Ib As BigInOut, Ob As BigInOut, ByVal N As Long, Iv As InOut, C As EncCtx) As Integer +Private Declare Function AesCbcDecrypt Lib "c:\temp\aes.dll" Alias "_aes_cbc_decrypt@20" _ + (Ib As BigInOut, Ob As BigInOut, ByVal N As Long, Iv As InOut, C As DecCtx) As Integer +Private Declare Function AesCfbEncrypt Lib "c:\temp\aes.dll" Alias "_aes_cfb_encrypt@20" _ + (Ib As BigInOut, Ob As BigInOut, ByVal N As Long, Iv As InOut, C As EncCtx) As Integer +Private Declare Function AesCfbDecrypt Lib "c:\temp\aes.dll" Alias "_aes_cfb_decrypt@20" _ + (Ib As BigInOut, Ob As BigInOut, ByVal N As Long, Iv As InOut, C As EncCtx) As Integer +Private Declare Function AesOfbCrypt Lib "c:\temp\aes.dll" Alias "_aes_ofb_crypt@20" _ + (Ib As BigInOut, Ob As BigInOut, ByVal N As Long, Iv As InOut, C As EncCtx) As Integer +Private Declare Function AesCtrCrypt Lib "c:\temp\aes.dll" Alias "_aes_ctr_crypt@24" _ + (Ib As BigInOut, Ob As BigInOut, ByVal N As Long, Iv As InOut, ByVal CtrFn As Long, C As EncCtx) As Integer + +Private Sub Hex(X As Byte) ' output a byte in hexadecimal format +Dim H As Byte +H = Int(X / 16) +If H < 10 Then Debug.Print Chr(48 + H); Else Debug.Print Chr(87 + H); +H = Int(X Mod 16) +If H < 10 Then Debug.Print Chr(48 + H); Else Debug.Print Chr(87 + H); +End Sub + +Private Sub OutKey(S As String, B As Key, ByVal KeyL As Integer) ' display a key value +Debug.Print: Debug.Print S; +For i = 0 To KeyL - 1 + Hex B.K(i) +Next i +End Sub + +Private Sub OutBlock(S As String, B As InOut) ' display an input/output block +Debug.Print: Debug.Print S; +For i = 0 To BlockLength - 1 + Hex B.IO(i) +Next i +End Sub + +Private Sub OutBigBlock(S As String, B As BigInOut) ' display an input/output block +Debug.Print: Debug.Print S; +For i = 0 To BlockLength - 1 + Hex B.IO(i) +Next i +Debug.Print " ... "; +For i = 127 * BlockLength To 128 * BlockLength - 1 + Hex B.IO(i) +Next i +End Sub + +Private Sub CtrInc(Ctr As InOut) + Ctr.IO(0) = Ctr.IO(0) + 1 + If (Ctr.IO(0) = 0) Then + Ctr.IO(1) = Ctr.IO(1) + 1 + If (Ctr.IO(1) = 0) Then + Ctr.IO(2) = Ctr.IO(2) + 1 + If (Ctr.IO(3) = 0) Then + Ctr.IO(3) = Ctr.IO(3) + 1 + End If + End If + End If +End Sub + +Rem The following Main routine should output the following in the immediate window: +Rem Variable Key Length ( 16 ) +Rem Key = 00000000000000000000000000000000 +Rem Input = 00000000000000000000000000000000 +Rem Encrypted Text = 66e94bd4ef8a2c3b884cfa59ca342b2e +Rem Decrypted Text = 00000000000000000000000000000000 +Rem Variable Key Length ( 24 ) +Rem Key = 000000000000000000000000000000000000000000000000 +Rem Input = 00000000000000000000000000000000 +Rem Encrypted Text = aae06992acbf52a3e8f4a96ec9300bd7 +Rem Decrypted Text = 00000000000000000000000000000000 +Rem Variable Key Length ( 32 ) +Rem Key = 0000000000000000000000000000000000000000000000000000000000000000 +Rem Input = 00000000000000000000000000000000 +Rem Encrypted Text = dc95c078a2408989ad48a21492842087 +Rem Decrypted Text = 00000000000000000000000000000000 +Rem Fixed Key Length ( 128 ) +Rem Key = 00000000000000000000000000000000 +Rem Input = 00000000000000000000000000000000 +Rem Encrypted Text = 66e94bd4ef8a2c3b884cfa59ca342b2e +Rem Decrypted Text = 00000000000000000000000000000000 +Rem Fixed Key Length ( 192 ) +Rem Key = 000000000000000000000000000000000000000000000000 +Rem Input = 00000000000000000000000000000000 +Rem Encrypted Text = aae06992acbf52a3e8f4a96ec9300bd7 +Rem Decrypted Text = 00000000000000000000000000000000 +Rem Fixed Key Length ( 256 ) +Rem Key = 0000000000000000000000000000000000000000000000000000000000000000 +Rem Input = 00000000000000000000000000000000 +Rem Encrypted Text = dc95c078a2408989ad48a21492842087 +Rem Decrypted Text = 00000000000000000000000000000000 + +Sub Main() +Dim Key As Key ' all these variables are initialised +Dim Ib As InOut, Ob As InOut, Rb As InOut ' to zero by VBA +Dim Iv1 As InOut, Iv2 As InOut +Dim Ecx As EncCtx +Dim Dcx As DecCtx +Dim RetVal As Integer + +For KeyL = 16 To 32 Step 8 +Debug.Print "Variable Key Length ("; KeyL; ")"; +OutKey "Key = ", Key, KeyL +OutBlock "Input = ", Ib +RetVal = AesEncryptKey(Key, KeyL, Ecx) ' set an all zero encryption key +RetVal = AesEncrypt(Ib, Ob, Ecx) ' encrypt Ib to Ob +OutBlock "Encrypted Text = ", Ob +RetVal = AesDecryptKey(Key, KeyL, Dcx) ' set an all zero decryption key +RetVal = AesDecrypt(Ob, Rb, Dcx) ' decrypt Ob to Rb +OutBlock "Decrypted Text = ", Rb +Debug.Print +Next KeyL + +Debug.Print +KeyL = 128: Debug.Print "Fixed Key Length ("; KeyL; ")"; +OutKey "Key = ", Key, 16 +OutBlock "Input = ", Ib +RetVal = AesEncryptKey128(Key, Ecx) ' set an all zero encryption key +RetVal = AesEncrypt(Ib, Ob, Ecx) ' encrypt Ib to Ob +OutBlock "Encrypted Text = ", Ob +RetVal = AesDecryptKey128(Key, Dcx) ' set an all zero decryption key +RetVal = AesDecrypt(Ob, Rb, Dcx) ' decrypt Ob to Rb +OutBlock "Decrypted Text = ", Rb +Debug.Print + +Debug.Print +KeyL = 192: Debug.Print "Fixed Key Length ("; KeyL; ")"; +OutKey "Key = ", Key, 24 +OutBlock "Input = ", Ib +RetVal = AesEncryptKey192(Key, Ecx) ' set an all zero encryption key +RetVal = AesEncrypt(Ib, Ob, Ecx) ' encrypt Ib to Ob +OutBlock "Encrypted Text = ", Ob +RetVal = AesDecryptKey192(Key, Dcx) ' set an all zero decryption key +RetVal = AesDecrypt(Ob, Rb, Dcx) ' decrypt Ob to Rb +OutBlock "Decrypted Text = ", Rb +Debug.Print + +Debug.Print +KeyL = 256: Debug.Print "Fixed Key Length ("; KeyL; ")"; +OutKey "Key = ", Key, 32 +OutBlock "Input = ", Ib +RetVal = AesEncryptKey256(Key, Ecx) ' set an all zero encryption key +RetVal = AesEncrypt(Ib, Ob, Ecx) ' encrypt Ib to Ob +OutBlock "Encrypted Text = ", Ob +RetVal = AesDecryptKey256(Key, Dcx) ' set an all zero decryption key +RetVal = AesDecrypt(Ob, Rb, Dcx) ' decrypt Ob to Rb +OutBlock "Decrypted Text = ", Rb +Debug.Print + +Debug.Print +KeyL = 128: Debug.Print "Fixed Key Length ("; KeyL; ")"; +OutKey "Key = ", Key, 16 +OutBlock "Input = ", Ib +RetVal = AesEncryptKey128(Key, Ecx) ' set an all zero encryption key +OutBlock "Encrypted Text = ", Ob +RetVal = AesDecryptKey128(Key, Dcx) ' set an all zero decryption key +OutBlock "Decrypted Text = ", Rb +Debug.Print + +Debug.Print +KeyL = 128: Debug.Print "Fixed Key Length ("; KeyL; ")"; +OutKey "Key = ", Key, 16 +RetVal = AesEncryptKey128(Key, Ecx) ' set an all zero encryption key +RetVal = AesDecryptKey128(Key, Dcx) ' set an all zero decryption key +Dim Pt1 As BigInOut, Pt2 As BigInOut, Ct As BigInOut + +For i = 0 To 128 * BlockLength - 1 + Pt1.IO(i) = i Mod 256 +Next i + +OutBigBlock "ECB Input = ", Pt1 +RetVal = AesEcbEncrypt(Pt1, Ct, 128 * BlockLength, Ecx) +OutBigBlock "Encrypted Text = ", Ct +RetVal = AesEcbDecrypt(Ct, Pt2, 128 * BlockLength, Dcx) +OutBigBlock "Decrypted Text = ", Pt2 +Debug.Print + +OutBigBlock "CBC Mode Input = ", Pt1 +RetVal = AesCbcEncrypt(Pt1, Ct, 128 * BlockLength, Iv1, Ecx) +OutBigBlock "Encrypted Text = ", Ct +RetVal = AesCbcDecrypt(Ct, Pt2, 128 * BlockLength, Iv2, Dcx) +OutBigBlock "Decrypted Text = ", Pt2 +Debug.Print + +OutBigBlock "CFB Mode Input = ", Pt1 +RetVal = AesCfbEncrypt(Pt1, Ct, 128 * BlockLength, Iv1, Ecx) +OutBigBlock "Encrypted Text = ", Ct +RetVal = AesCfbDecrypt(Ct, Pt2, 128 * BlockLength, Iv2, Ecx) +OutBigBlock "Decrypted Text = ", Pt2 +Debug.Print + +OutBigBlock "OFB Mode Input = ", Pt1 +RetVal = AesOfbCrypt(Pt1, Ct, 128 * BlockLength, Iv1, Ecx) +OutBigBlock "Encrypted Text = ", Ct +RetVal = AesOfbCrypt(Ct, Pt2, 128 * BlockLength, Iv2, Ecx) +OutBigBlock "Decrypted Text = ", Pt2 +Debug.Print + +#If False Then +Rem CTR Mode is not working because of a problem with the 'AddressOf' operator +OutBigBlock "CTR Mode Input = ", Pt1 +RetVal = AesCtrCrypt(Pt1, Ct, 128 * BlockLength, Iv1, AddressOf CtrInc, Ecx) +OutBigBlock "Encrypted Text = ", Ct +RetVal = AesCtrCrypt(Ct, Pt2, 128 * BlockLength, Iv2, AddressOf CtrInc, Ecx) +OutBigBlock "Decrypted Text = ", Pt2 +Debug.Print +#End If + +Debug.Print +End Sub diff --git a/src/java/KP2AKdbLibrary/jni/aes/vbaxam.doc b/src/java/KP2AKdbLibrary/jni/aes/vbaxam.doc new file mode 100644 index 00000000..c3461ff8 Binary files /dev/null and b/src/java/KP2AKdbLibrary/jni/aes/vbaxam.doc differ diff --git a/src/java/KP2AKdbLibrary/jni/aes/via_ace.txt b/src/java/KP2AKdbLibrary/jni/aes/via_ace.txt new file mode 100644 index 00000000..55c802ec --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/aes/via_ace.txt @@ -0,0 +1,158 @@ + +Support for the VIA Nehemiah Advanced Cryptography Engine (ACE) +--------------------------------------------------------------- + +A. Introduction + +The AES code now supports the VIA ACE engine. The engine is invoked by the +multiple block AES modes calls in aes_modes.c and not by the basic AES code. + +The define USE_VIA_ACE_IF_PRESENT is defined if VIA ACE detection and use is +required with fallback to the normal AES code if it is not present. + +The define ASSUME_VIA_ACE_PRESENT is used when it is known that the VIA ACE +engine will always be present. Note, however, that this code will not work +correctly if the VIA ACE engine is either not present or turned off. + +To enable ACE support the appropriate defines in section 2 of the options in +aesopt.h must be set. If ACE support is required then key scheduling must +use the C code so only the generic C code in Win32 mode, ASM_X86_V1C and +ASM_X86_V2C assembler code can be used (i.e ASM_X86_V2 and ASM_AMD64_C do +NOT support VIA ACE). + +B. Using ACE + +ACE is used in the code that implements the subroutines used for the multiple +block AES modes defined in aes_modes.h: + + // used to reset modes to their start point without entering a new key + AES_RETURN aes_mode_reset(aes_encrypt_ctx cx[1]); + + AES_RETURN aes_ecb_encrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, const aes_encrypt_ctx cx[1]); + + AES_RETURN aes_ecb_decrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, const aes_decrypt_ctx cx[1]); + + AES_RETURN aes_cbc_encrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, const aes_encrypt_ctx cx[1]); + + AES_RETURN aes_cbc_decrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, const aes_decrypt_ctx cx[1]); + + AES_RETURN aes_cfb_encrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, aes_encrypt_ctx cx[1]); + + AES_RETURN aes_cfb_decrypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, aes_encrypt_ctx cx[1]); + + #define aes_ofb_encrypt aes_ofb_crypt + #define aes_ofb_decrypt aes_ofb_crypt + + AES_RETURN aes_ofb_crypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *iv, aes_encrypt_ctx cx[1]); + + typedef void cbuf_inc(unsigned char *cbuf); + + #define aes_ctr_encrypt aes_ctr_crypt + #define aes_ctr_decrypt aes_ctr_crypt + + AES_RETURN aes_ctr_crypt(const unsigned char *ibuf, unsigned char *obuf, + int len, unsigned char *cbuf, cbuf_inc ctr_inc, aes_encrypt_ctx cx[1]); + +Note that the single block AES calls defined in aes.h: + + AES_RETURN aes_encrypt(const unsigned char *in, unsigned char *out, + const aes_encrypt_ctx cx[1]); + + AES_RETURN aes_decrypt(const unsigned char *in, unsigned char *out, + const aes_decrypt_ctx cx[1]); + +do NOT provide ACE support and should not be used if the ACE engine is +available and ACE support is required. + +C. Constraints and Optimisation + +There are several constraints that have to be observed when ACE is used if +the best performance is to be achieved: + +1. As usual the appropriate key set up subroutine must be called before any + of the above subroutines are used. + +2. The AES contexts - aes_encryption_ctx and aes_decryption_ctx - used with + these subroutines MUST be 16 byte aligned. Failure to align AES contexts + will often cause memory alignment exceptions. + +3. The buffers used for inputs, outputs and IVs do not need to be 16 byte + aligned but the speed that is achieved will be much higher if this can be + arranged. In a flat address space (as now typical in 32-bit systems) this + means that: (a) that the lower nibble of all buffer addresses must be + zero, and (b) the compiler used must arrange to load the data and stack + segments on 16 byte address boundaries. The Microsoft VC++ compiler can + align all variables in this way (see the example macros for doing this in + aes_via_ace.txt). However it seems that the GCC compiler will only do this + for static global variables but not for variables placed on the stack, that + is local variables. + +4. The data length in bytes (len) in calls to the ECB and CBC subroutines + must be a multiple of the 16 byte block length. An error return will + occur if this is not so. + +5. The data length in all calls to the CFB, OFB and CTR subroutines must also + be a multiple of 16 bytes if the VIA ACE engine is to be used. Otherwise + these lengths can be of any value but the subroutines will only proceed at + full speed for lengths that are multiples of 16 bytes. The CFB, OFB and + CTR subroutines are incremental, with subsequent calls continuing from + where previous calls finished. The subroutine aes_mode_reset() can be used + to restart a mode without a key change but is not needed after a new key is + entered. Such a reset is not needed when the data lengths in all individual + calls to the AES mode subroutines are multiples of 16 bytes. + +6. Note that the AES context contains mode details so only one type of mode + can be run from a context at any one time. A reset is necessary if a new + mode is used without a new context or a new key. + +D. Expected Speeds + +The speeds that have been obtained using a 1.2 GHz VIA C3 processor with +this code are given below (note that since CTR mode is not available in +the VIA hardware it is not present in the aligned timing figures): + +AES Timing (Cycles/Byte) with the VIA ACE Engine (aligned in C) +Mode Blocks: 1 10 100 1000 Peak Throughput +ecb encrypt 8.25 1.36 0.69 0.63 1.9 Gbytes/second +ecb decrypt 8.75 1.41 0.70 0.64 1.9 Gbytes/second +cbc encrypt 11.56 2.41 1.47 1.38 870 Mbytes/second +cbc decrypt 12.37 2.38 1.47 1.38 870 Mbytes/second +cfb encrypt 11.93 2.46 1.48 1.38 870 Mbytes/second +cfb decrypt 12.18 2.36 1.47 1.38 870 Mbytes/second +ofb encrypt 13.31 3.88 2.92 2.82 425 Mbytes/second +ofb decrypt 13.31 3.88 2.92 2.82 425 Mbytes/second + +AES Timing (Cycles/Byte) with the VIA ACE Engine (unaligned in C) +Mode Blocks: 1 10 100 1000 Peak Throughput +ecb encrypt 17.68 4.31 3.15 3.05 390 Mbytes/second +ecb decrypt 18.12 4.36 3.17 3.06 390 Mbytes/second +cbc encrypt 20.68 5.70 4.39 4.27 280 Mbytes/second +cbc decrypt 21.87 5.75 4.34 4.21 285 Mbytes/second +cfb encrypt 21.06 5.81 4.43 4.31 280 Mbytes/second +cfb decrypt 21.37 5.72 4.36 4.24 285 Mbytes/second +ofb encrypt 22.43 7.23 5.85 5.72 210 Mbytes/second +ofb decrypt 22.43 7.34 5.86 5.73 210 Mbytes/second +ctr encrypt 16.43 6.90 6.00 5.89 205 Mbytes/second +ctr decrypt 16.43 6.90 6.00 5.89 205 Mbytes/second + +AES Timing (Cycles/Byte) with the VIA ACE Engine (unaligned assembler) +Mode Blocks: 1 10 100 1000 Peak Throughput +ecb encrypt 11.87 2.89 1.91 1.83 660 Mbytes/second +ecb decrypt 12.18 2.83 1.97 1.87 640 Mbytes/second +cbc encrypt 14.87 4.13 3.11 3.01 400 Mbytes/second +cbc decrypt 14.43 3.87 2.89 2.80 430 Mbytes/second +cfb encrypt 14.75 4.12 3.10 3.01 400 Mbytes/second +cfb decrypt 14.12 4.10 2.88 2.79 430 Mbytes/second +ofb encrypt 15.25 5.36 4.37 4.27 280 Mbytes/second +ofb decrypt 15.25 5.36 4.36 4.27 280 Mbytes/second +ctr encrypt 13.31 4.79 4.01 3.94 305 Mbytes/second +ctr decrypt 13.31 4.79 4.01 3.94 305 Mbytes/second + + Brian Gladman, Worcester, UK diff --git a/src/java/KP2AKdbLibrary/jni/final_key/Android.mk b/src/java/KP2AKdbLibrary/jni/final_key/Android.mk new file mode 100644 index 00000000..5c460e78 --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/final_key/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := final-key + +LOCAL_SRC_FILES := \ + kpd_jni.c + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/../sha $(LOCAL_PATH)/../aes + +LOCAL_STATIC_LIBRARIES := aes sha + +LOCAL_LDLIBS := -llog + +include $(BUILD_SHARED_LIBRARY) diff --git a/src/java/KP2AKdbLibrary/jni/final_key/kpd_jni.c b/src/java/KP2AKdbLibrary/jni/final_key/kpd_jni.c new file mode 100644 index 00000000..7d4b9292 --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/final_key/kpd_jni.c @@ -0,0 +1,507 @@ +/* + This is a JNI wrapper for AES & SHA source code on Android. + Copyright (C) 2010 Michael Mohr + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include + +/* Tune as desired */ +#undef KPD_PROFILE +#undef KPD_DEBUG + +#if defined(KPD_PROFILE) +#include +#endif + +#if defined(KPD_DEBUG) +#include +#endif + +#include "aes.h" +#include "sha2.h" + +static JavaVM *cached_vm; +static jclass bad_arg, no_mem, bad_padding, short_buf, block_size; + +typedef enum { + ENCRYPTION, + DECRYPTION, + FINALIZED +} edir_t; + +#define AES_BLOCK_SIZE 16 +#define CACHE_SIZE 32 + +typedef struct _aes_state { + edir_t direction; + uint32_t cache_len; + uint8_t iv[16], cache[CACHE_SIZE]; + uint8_t ctx[sizeof(aes_encrypt_ctx)]; // 244 +} aes_state; + +#define ENC_CTX(state) (((aes_encrypt_ctx *)((state)->ctx))) +#define DEC_CTX(state) (((aes_decrypt_ctx *)((state)->ctx))) +#define ALIGN_EXTRA 15 +#define ALIGN16(x) (void *)(((uintptr_t)(x)+ALIGN_EXTRA) & ~ 0x0F) + +JNIEXPORT jint JNICALL JNI_OnLoad( JavaVM *vm, void *reserved ) { + JNIEnv *env; + jclass cls; + + cached_vm = vm; + if((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_6)) + return JNI_ERR; + + cls = (*env)->FindClass(env, "java/lang/IllegalArgumentException"); + if( cls == NULL ) + return JNI_ERR; + bad_arg = (*env)->NewGlobalRef(env, cls); + if( bad_arg == NULL ) + return JNI_ERR; + + cls = (*env)->FindClass(env, "java/lang/OutOfMemoryError"); + if( cls == NULL ) + return JNI_ERR; + no_mem = (*env)->NewGlobalRef(env, cls); + if( no_mem == NULL ) + return JNI_ERR; + + cls = (*env)->FindClass(env, "javax/crypto/BadPaddingException"); + if( cls == NULL ) + return JNI_ERR; + bad_padding = (*env)->NewGlobalRef(env, cls); + + cls = (*env)->FindClass(env, "javax/crypto/ShortBufferException"); + if( cls == NULL ) + return JNI_ERR; + short_buf = (*env)->NewGlobalRef(env, cls); + + cls = (*env)->FindClass(env, "javax/crypto/IllegalBlockSizeException"); + if( cls == NULL ) + return JNI_ERR; + block_size = (*env)->NewGlobalRef(env, cls); + + aes_init(); + + return JNI_VERSION_1_6; +} + +// called on garbage collection +JNIEXPORT void JNICALL JNI_OnUnload( JavaVM *vm, void *reserved ) { + JNIEnv *env; + if((*vm)->GetEnv(vm, (void **)&env, JNI_VERSION_1_6)) { + return; + } + (*env)->DeleteGlobalRef(env, bad_arg); + (*env)->DeleteGlobalRef(env, no_mem); + (*env)->DeleteGlobalRef(env, bad_padding); + (*env)->DeleteGlobalRef(env, short_buf); + (*env)->DeleteGlobalRef(env, block_size); + return; +} + +JNIEXPORT jlong JNICALL Java_com_keepassdroid_crypto_NativeAESCipherSpi_nInit(JNIEnv *env, jobject this, jboolean encrypting, jbyteArray key, jbyteArray iv) { + uint8_t ckey[32]; + aes_state *state; + jint key_len = (*env)->GetArrayLength(env, key); + jint iv_len = (*env)->GetArrayLength(env, iv); + + if( ! ( key_len == 16 || key_len == 24 || key_len == 32 ) || iv_len != 16 ) { + (*env)->ThrowNew(env, bad_arg, "Invalid length of key or iv"); + return -1; + } + + state = (aes_state *)malloc(sizeof(aes_state)); + if( state == NULL ) { + (*env)->ThrowNew(env, no_mem, "Cannot allocate memory for the encryption state"); + return -1; + } + memset(state, 0, sizeof(aes_state)); + + (*env)->GetByteArrayRegion(env, key, (jint)0, key_len, (jbyte *)ckey); + (*env)->GetByteArrayRegion(env, iv, (jint)0, iv_len, (jbyte *)state->iv); + + if( encrypting ) { + state->direction = ENCRYPTION; + aes_encrypt_key(ckey, key_len, ENC_CTX(state)); + } else { + state->direction = DECRYPTION; + aes_decrypt_key(ckey, key_len, DEC_CTX(state)); + } + + return (jlong)state; +} + +JNIEXPORT void JNICALL Java_com_keepassdroid_crypto_NativeAESCipherSpi_nCleanup(JNIEnv *env, jclass this, jlong state) { + free((void *)state); +} + +/* + TODO: + It seems like the android implementation of the AES cipher stays a + block behind with update calls. So, if you do an update for 16 bytes, + it will return nothing in the output buffer. Then, it is the finalize + call that will return the last block stripping off padding if it is + not a full block. +*/ + +JNIEXPORT jint JNICALL Java_com_keepassdroid_crypto_NativeAESCipherSpi_nUpdate(JNIEnv *env, jobject this, + jlong state, jbyteArray input, jint inputOffset, jint inputLen, jbyteArray output, jint outputOffset, jint outputSize) { + int aes_ret; + uint32_t outLen, bytes2cache, cryptLen; + void *in, *out; + uint8_t *c_input, *c_output; + aes_state *c_state; + + #if defined(KPD_DEBUG) + __android_log_print(ANDROID_LOG_INFO, "kpd_jni.c/nUpdate", "entry: inputLen=%d, outputSize=%d", inputLen, outputSize); + #endif + + // step 1: first, some housecleaning + if( !inputLen || !outputSize || outputOffset < 0 || !input || !output ) { + (*env)->ThrowNew(env, bad_arg, "nUpdate: called with 1 or more invalid arguments"); + return -1; + } + c_state = (aes_state *)state; + if( c_state->direction == FINALIZED ) { + (*env)->ThrowNew(env, bad_arg, "Trying to update a finalized state"); + return -1; + } + + // step 1.5: calculate cryptLen and outLen + cryptLen = inputLen + c_state->cache_len; + if( cryptLen < CACHE_SIZE ) { + (*env)->GetByteArrayRegion(env, input, inputOffset, inputLen, (jbyte *)(c_state->cache + c_state->cache_len)); + c_state->cache_len = cryptLen; + return 0; + } + // now we're guaranteed that cryptLen >= CACHE_SIZE (32) + bytes2cache = (cryptLen & 15) + AES_BLOCK_SIZE; // mask bottom 4 bits plus 1 block + outLen = (cryptLen - bytes2cache); // output length is now aligned to a 16-byte boundary + if( outLen > (uint32_t)outputSize ) { + (*env)->ThrowNew(env, bad_arg, "Output buffer does not have enough space"); + return -1; + } + + // step 2: allocate memory to hold input and output data + in = malloc(cryptLen+ALIGN_EXTRA); + if( in == NULL ) { + (*env)->ThrowNew(env, no_mem, "Unable to allocate heap space for encryption input"); + return -1; + } + c_input = ALIGN16(in); + + out = malloc(outLen+ALIGN_EXTRA); + if( out == NULL ) { + free(in); + (*env)->ThrowNew(env, no_mem, "Unable to allocate heap space for encryption output"); + return -1; + } + c_output = ALIGN16(out); + + // step 3: copy data from Java and en/decrypt it + if( c_state->cache_len ) { + memcpy(c_input, c_state->cache, c_state->cache_len); + (*env)->GetByteArrayRegion(env, input, inputOffset, inputLen, (jbyte *)(c_input + c_state->cache_len)); + } else { + (*env)->GetByteArrayRegion(env, input, inputOffset, inputLen, (jbyte *)c_input); + } + if( c_state->direction == ENCRYPTION ) + aes_ret = aes_cbc_encrypt(c_input, c_output, outLen, c_state->iv, ENC_CTX(c_state)); + else + aes_ret = aes_cbc_decrypt(c_input, c_output, outLen, c_state->iv, DEC_CTX(c_state)); + if( aes_ret != EXIT_SUCCESS ) { + free(in); + free(out); + (*env)->ThrowNew(env, bad_arg, "Failed to encrypt input data"); // FIXME: get a better exception class for this... + return -1; + } + (*env)->SetByteArrayRegion(env, output, outputOffset, outLen, (jbyte *)c_output); + + // step 4: cleanup and return + if( bytes2cache ) { + c_state->cache_len = bytes2cache; // set new cache length + memcpy(c_state->cache, (c_input + outLen), bytes2cache); // cache overflow bytes for next call + } else { + c_state->cache_len = 0; + } + + free(in); + free(out); + + #if defined(KPD_DEBUG) + __android_log_print(ANDROID_LOG_INFO, "kpd_jni.c/nUpdate", "exit: outLen=%d", outLen); + #endif + + return outLen; +} + +/* + outputSize must be at least 32 for encryption since the buffer may contain >= 1 full block + outputSize must be at least 16 for decryption +*/ +JNIEXPORT jint JNICALL Java_com_keepassdroid_crypto_NativeAESCipherSpi_nFinal(JNIEnv *env, jobject this, + jlong state, jboolean doPadding, jbyteArray output, jint outputOffset, jint outputSize) { + int i; + uint32_t padValue, paddedCacheLen, j; + uint8_t final_output[CACHE_SIZE] __attribute__ ((aligned (16))); + aes_state *c_state; + + #if defined(KPD_DEBUG) + __android_log_print(ANDROID_LOG_INFO, "kpd_jni.c/nFinal", "entry: outputOffset=%d, outputSize=%d", outputOffset, outputSize); + #endif + + if( !output || outputOffset < 0 ) { + (*env)->ThrowNew(env, bad_arg, "Invalid argument(s) passed to nFinal"); + return -1; + } + c_state = (aes_state *)state; + if( c_state->direction == FINALIZED ) { + (*env)->ThrowNew(env, bad_arg, "This state has already been finalized"); + return -1; + } + + // allow fetching of remaining bytes from cache + if( !doPadding ) { + (*env)->SetByteArrayRegion(env, output, outputOffset, c_state->cache_len, (jbyte *)c_state->cache); + c_state->direction = FINALIZED; + return c_state->cache_len; + } + + #if defined(KPD_DEBUG) + __android_log_print(ANDROID_LOG_INFO, "kpd_jni.c/nFinal", "crypto operation starts"); + #endif + + if( c_state->direction == ENCRYPTION ) { + if( c_state->cache_len >= 16 ) { + paddedCacheLen = 32; + } else { + paddedCacheLen = 16; + } + if( outputSize < (jint)paddedCacheLen ) { + (*env)->ThrowNew(env, short_buf, "Insufficient space in output buffer"); + return -1; + } + padValue = paddedCacheLen - c_state->cache_len; + if(!padValue) padValue = 16; + memset(c_state->cache + c_state->cache_len, padValue, padValue); + if( aes_cbc_encrypt(c_state->cache, final_output, paddedCacheLen, c_state->iv, ENC_CTX(c_state)) != EXIT_SUCCESS ) { + (*env)->ThrowNew(env, bad_arg, "Failed to encrypt the final data block(s)"); // FIXME: get a better exception class for this... + return -1; + } + (*env)->SetByteArrayRegion(env, output, outputOffset, paddedCacheLen, (jbyte *)final_output); + c_state->direction = FINALIZED; + #if defined(KPD_DEBUG) + __android_log_print(ANDROID_LOG_INFO, "kpd_jni.c/nFinal", "encryption operation completed, returning %d bytes", paddedCacheLen); + #endif + return paddedCacheLen; + } else { // DECRYPTION + paddedCacheLen = c_state->cache_len; + if( outputSize < (jint)paddedCacheLen ) { + (*env)->ThrowNew(env, short_buf, "Insufficient space in output buffer"); + return -1; + } + if( paddedCacheLen != 16 ) { + (*env)->ThrowNew(env, bad_padding, "Incomplete final block in cache for decryption state"); + return -1; + } + if( aes_cbc_decrypt(c_state->cache, final_output, paddedCacheLen, c_state->iv, DEC_CTX(c_state)) != EXIT_SUCCESS ) { + (*env)->ThrowNew(env, bad_arg, "Failed to decrypt the final data block(s)"); // FIXME: get a better exception class for this... + return -1; + } + padValue = final_output[paddedCacheLen-1]; + for(i = (paddedCacheLen-1), j = 0; final_output[i] == padValue && i >= 0; i--, j++); + if( padValue != j ) { + (*env)->ThrowNew(env, bad_padding, "Failed to verify padding during decryption"); + return -1; + } + j = 16 - j; + (*env)->SetByteArrayRegion(env, output, outputOffset, j, (jbyte *)final_output); + c_state->direction = FINALIZED; + #if defined(KPD_DEBUG) + __android_log_print(ANDROID_LOG_INFO, "kpd_jni.c/nFinal", "decryption operation completed, returning %d bytes", j); + #endif + return j; + } +} + +JNIEXPORT jint JNICALL Java_com_keepassdroid_crypto_NativeAESCipherSpi_nGetCacheSize(JNIEnv* env, jobject this, jlong state) { + aes_state *c_state; + + c_state = (aes_state *)state; + if( c_state->direction == FINALIZED ) { + (*env)->ThrowNew(env, bad_arg, "Invalid state"); + return -1; + } + return c_state->cache_len; +} + +#define MASTER_KEY_SIZE 32 + +typedef struct _master_key { + uint32_t rounds, done[2]; + pthread_mutex_t lock1, lock2; // these lock the two halves of the key material + uint8_t c_seed[MASTER_KEY_SIZE] __attribute__ ((aligned (16))); + uint8_t key1[MASTER_KEY_SIZE] __attribute__ ((aligned (16))); + uint8_t key2[MASTER_KEY_SIZE] __attribute__ ((aligned (16))); +} master_key; + + +void *generate_key_material(void *arg) { + #if defined(KPD_PROFILE) + struct timespec start, end; + #endif + uint32_t i, flip = 0; + uint8_t *key1, *key2; + master_key *mk = (master_key *)arg; + aes_encrypt_ctx e_ctx[1] __attribute__ ((aligned (16))); + + if( mk->done[0] == 0 && pthread_mutex_trylock(&mk->lock1) == 0 ) { + key1 = mk->key1; + key2 = mk->key2; + } else if( mk->done[1] == 0 && pthread_mutex_trylock(&mk->lock2) == 0 ) { + key1 = mk->key1 + (MASTER_KEY_SIZE/2); + key2 = mk->key2 + (MASTER_KEY_SIZE/2); + } else { + // this can only be scaled to two threads + pthread_exit( (void *)(-1) ); + } + + #if defined(KPD_PROFILE) + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &start); + #endif + + aes_encrypt_key256(mk->c_seed, e_ctx); + for (i = 0; i < mk->rounds; i++) { + if ( flip ) { + aes_encrypt(key2, key1, e_ctx); + flip = 0; + } else { + aes_encrypt(key1, key2, e_ctx); + flip = 1; + } + } + + #if defined(KPD_PROFILE) + clock_gettime(CLOCK_THREAD_CPUTIME_ID, &end); + if( key1 == mk->key1 ) + __android_log_print(ANDROID_LOG_INFO, "kpd_jni.c/nTransformMasterKey", "Thread 1 master key transformation took ~%d seconds", (end.tv_sec-start.tv_sec)); + else + __android_log_print(ANDROID_LOG_INFO, "kpd_jni.c/nTransformMasterKey", "Thread 2 master key transformation took ~%d seconds", (end.tv_sec-start.tv_sec)); + #endif + + if( key1 == mk->key1 ) { + mk->done[0] = 1; + pthread_mutex_unlock(&mk->lock1); + } else { + mk->done[1] = 1; + pthread_mutex_unlock(&mk->lock2); + } + + return (void *)flip; +} + +JNIEXPORT jbyteArray JNICALL Java_com_keepassdroid_crypto_finalkey_NativeFinalKey_nTransformMasterKey(JNIEnv *env, jobject this, jbyteArray seed, jbyteArray key, jint rounds) { + master_key mk; + uint32_t flip; + pthread_t t1, t2; + int iret; + void *vret1, *vret2; + jbyteArray result; + sha256_ctx h_ctx[1] __attribute__ ((aligned (16))); + + // step 1: housekeeping - sanity checks and fetch data from the JVM + if( (*env)->GetArrayLength(env, seed) != MASTER_KEY_SIZE ) { + (*env)->ThrowNew(env, bad_arg, "TransformMasterKey: the seed is not the correct size"); + return NULL; + } + if( (*env)->GetArrayLength(env, key) != MASTER_KEY_SIZE ) { + (*env)->ThrowNew(env, bad_arg, "TransformMasterKey: the key is not the correct size"); + return NULL; + } + if( rounds < 0 ) { + (*env)->ThrowNew(env, bad_arg, "TransformMasterKey: illegal number of encryption rounds"); + return NULL; + } + mk.rounds = (uint32_t)rounds; + mk.done[0] = mk.done[1] = 0; + if( pthread_mutex_init(&mk.lock1, NULL) != 0 ) { + (*env)->ThrowNew(env, bad_arg, "TransformMasterKey: failed to initialize the mutex for thread 1"); // FIXME: get a better exception class for this... + return NULL; + } + if( pthread_mutex_init(&mk.lock2, NULL) != 0 ) { + (*env)->ThrowNew(env, bad_arg, "TransformMasterKey: failed to initialize the mutex for thread 2"); // FIXME: get a better exception class for this... + return NULL; + } + (*env)->GetByteArrayRegion(env, seed, 0, MASTER_KEY_SIZE, (jbyte *)mk.c_seed); + (*env)->GetByteArrayRegion(env, key, 0, MASTER_KEY_SIZE, (jbyte *)mk.key1); + + // step 2: encrypt the hash "rounds" (default: 6000) times + iret = pthread_create( &t1, NULL, generate_key_material, (void*)&mk ); + if( iret != 0 ) { + (*env)->ThrowNew(env, bad_arg, "TransformMasterKey: failed to launch thread 1"); // FIXME: get a better exception class for this... + return NULL; + } + iret = pthread_create( &t2, NULL, generate_key_material, (void*)&mk ); + if( iret != 0 ) { + (*env)->ThrowNew(env, bad_arg, "TransformMasterKey: failed to launch thread 2"); // FIXME: get a better exception class for this... + return NULL; + } + iret = pthread_join( t1, &vret1 ); + if( iret != 0 ) { + (*env)->ThrowNew(env, bad_arg, "TransformMasterKey: failed to join thread 1"); // FIXME: get a better exception class for this... + return NULL; + } + iret = pthread_join( t2, &vret2 ); + if( iret != 0 ) { + (*env)->ThrowNew(env, bad_arg, "TransformMasterKey: failed to join thread 2"); // FIXME: get a better exception class for this... + return NULL; + } + if( vret1 == (void *)(-1) || vret2 == (void *)(-1) || vret1 != vret2 ) { + (*env)->ThrowNew(env, bad_arg, "TransformMasterKey: invalid flip value(s) from completed thread(s)"); // FIXME: get a better exception class for this... + return NULL; + } else { + flip = (uint32_t)vret1; + } + + // step 3: final SHA256 hash + sha256_begin(h_ctx); + if( flip ) { + sha256_hash(mk.key2, MASTER_KEY_SIZE, h_ctx); + sha256_end(mk.key1, h_ctx); + flip = 0; + } else { + sha256_hash(mk.key1, MASTER_KEY_SIZE, h_ctx); + sha256_end(mk.key2, h_ctx); + flip = 1; + } + + // step 4: send the hash into the JVM + result = (*env)->NewByteArray(env, MASTER_KEY_SIZE); + if( flip ) + (*env)->SetByteArrayRegion(env, result, 0, MASTER_KEY_SIZE, (jbyte *)mk.key2); + else + (*env)->SetByteArrayRegion(env, result, 0, MASTER_KEY_SIZE, (jbyte *)mk.key1); + + return result; +} +#undef MASTER_KEY_SIZE + diff --git a/src/java/KP2AKdbLibrary/jni/prep_build.sh b/src/java/KP2AKdbLibrary/jni/prep_build.sh new file mode 100644 index 00000000..057a15e4 --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/prep_build.sh @@ -0,0 +1,5 @@ +#!/bin/sh +SHA_FILE="sha2-07-01-07.zip" + +curl http://gladman.plushost.co.uk/oldsite/cryptography_technology/sha/$SHA_FILE > $SHA_FILE +unzip $SHA_FILE -d sha diff --git a/src/java/KP2AKdbLibrary/jni/sha/.gitignore b/src/java/KP2AKdbLibrary/jni/sha/.gitignore new file mode 100644 index 00000000..18596cbc --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/sha/.gitignore @@ -0,0 +1,13 @@ +brg_endian.h +brg_types.h +hmac.c +hmac.h +pwd2key.c +pwd2key.h +sha1b.c +sha1.c +sha1.h +sha2b.c +sha2.c +sha2.h +shasum.c diff --git a/src/java/KP2AKdbLibrary/jni/sha/Android.mk b/src/java/KP2AKdbLibrary/jni/sha/Android.mk new file mode 100644 index 00000000..bfdad4e5 --- /dev/null +++ b/src/java/KP2AKdbLibrary/jni/sha/Android.mk @@ -0,0 +1,14 @@ +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_MODULE := sha + +LOCAL_SRC_FILES := \ + sha1.c \ + sha2.c \ + hmac.c + +LOCAL_CFLAGS := -DUSE_SHA256 + +include $(BUILD_STATIC_LIBRARY) diff --git a/src/java/KP2AKdbLibrary/libs/.gitignore b/src/java/KP2AKdbLibrary/libs/.gitignore new file mode 100644 index 00000000..f1a2c82c --- /dev/null +++ b/src/java/KP2AKdbLibrary/libs/.gitignore @@ -0,0 +1,8 @@ +[^.] +armeabi +armeabi-v7a +x86 +mips +mips-r2 +mips-r2-sf +ant-contrib-0.3.jar diff --git a/src/java/KP2AKdbLibrary/project.properties b/src/java/KP2AKdbLibrary/project.properties new file mode 100644 index 00000000..b83fc3aa --- /dev/null +++ b/src/java/KP2AKdbLibrary/project.properties @@ -0,0 +1,15 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system use, +# "ant.properties", and override values to adapt the script to your +# project structure. + +# Indicates whether an apk should be generated for each density. +split.density=false +# Project target. +target=android-17 +apk-configurations= +android.library=true diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/UpdateStatus.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/UpdateStatus.java new file mode 100644 index 00000000..e5061ebd --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/UpdateStatus.java @@ -0,0 +1,59 @@ +/* + * Copyright 2009 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid; + +import android.app.ProgressDialog; +import android.content.Context; +import android.os.Handler; + +public class UpdateStatus { + private ProgressDialog mPD; + private Context mCtx; + private Handler mHandler; + + public UpdateStatus() { + + } + + public UpdateStatus(Context ctx, Handler handler, ProgressDialog pd) { + mCtx = ctx; + mPD = pd; + mHandler = handler; + } + + public void updateMessage(int resId) { + if ( mCtx != null && mPD != null && mHandler != null ) { + mHandler.post(new UpdateMessage(resId)); + } + } + + private class UpdateMessage implements Runnable { + private int mResId; + + public UpdateMessage(int resId) { + mResId = resId; + } + + public void run() { + mPD.setMessage(mCtx.getString(mResId)); + } + + } +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/AESProvider.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/AESProvider.java new file mode 100644 index 00000000..32a8cd66 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/AESProvider.java @@ -0,0 +1,36 @@ +/* + * Copyright 2009 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.crypto; + +import java.security.Provider; + +public final class AESProvider extends Provider { + + /** + * + */ + private static final long serialVersionUID = -3846349284296062658L; + + public AESProvider() { + super("AESProvider", 1.0, ""); + put("Cipher.AES",com.keepassdroid.crypto.NativeAESCipherSpi.class.getName()); + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/BouncyCastleProvider.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/BouncyCastleProvider.java new file mode 100644 index 00000000..89f35a7f --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/BouncyCastleProvider.java @@ -0,0 +1,36 @@ +/* + * Copyright 2010 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.crypto; + +import java.security.Provider; + +public class BouncyCastleProvider extends Provider { + /** + * + */ + private static final long serialVersionUID = -6705090615178002994L; + + public BouncyCastleProvider() { + super("BouncyCastleProvider", 1.0, ""); + + put("Cipher.TWOFISH", org.bouncycastle.jce.provider.JCEBlockCipher.Twofish.class.getName()); + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/CipherFactory.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/CipherFactory.java new file mode 100644 index 00000000..68c74135 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/CipherFactory.java @@ -0,0 +1,104 @@ +/* + * Copyright 2010-2013 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.crypto; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.UUID; + +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import android.os.Build; + +import com.keepassdroid.utils.Types; + +public class CipherFactory { + private static boolean blacklistInit = false; + private static boolean blacklisted; + + public static Cipher getInstance(String transformation) throws NoSuchAlgorithmException, NoSuchPaddingException { + return getInstance(transformation, false); + } + + public static Cipher getInstance(String transformation, boolean androidOverride) throws NoSuchAlgorithmException, NoSuchPaddingException { + // Return the native AES if it is possible + if ( (!deviceBlacklisted()) && (!androidOverride) && hasNativeImplementation(transformation) && NativeLib.loaded() ) { + return Cipher.getInstance(transformation, new AESProvider()); + } else { + try { + return Cipher.getInstance(transformation, new BouncyCastleProvider()); + } catch (NoSuchAlgorithmException e) { + // Do nothing, fall through + } catch (NoSuchPaddingException e) { + // Do nothing, fall through + } + return Cipher.getInstance(transformation); + } + } + + public static boolean deviceBlacklisted() { + if (!blacklistInit) { + blacklistInit = true; + + // The Acer Iconia A500 is special and seems to always crash in the native crypto libraries + blacklisted = Build.MODEL.equals("A500"); + } + return blacklisted; + } + + private static boolean hasNativeImplementation(String transformation) { + return transformation.equals("AES/CBC/PKCS5Padding"); + } + + + public static final UUID AES_CIPHER = Types.bytestoUUID( + new byte[]{(byte)0x31, (byte)0xC1, (byte)0xF2, (byte)0xE6, (byte)0xBF, (byte)0x71, (byte)0x43, (byte)0x50, + (byte)0xBE, (byte)0x58, (byte)0x05, (byte)0x21, (byte)0x6A, (byte)0xFC, 0x5A, (byte)0xFF + }); + + + /** Generate appropriate cipher based on KeePass 2.x UUID's + * @param uuid + * @return + * @throws NoSuchPaddingException + * @throws NoSuchAlgorithmException + * @throws InvalidAlgorithmParameterException + * @throws InvalidKeyException + */ + public static Cipher getInstance(UUID uuid, int opmode, byte[] key, byte[] IV) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException { + return getInstance(uuid, opmode, key, IV, false); + } + + public static Cipher getInstance(UUID uuid, int opmode, byte[] key, byte[] IV, boolean androidOverride) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException { + if ( uuid.equals(AES_CIPHER) ) { + Cipher cipher = CipherFactory.getInstance("AES/CBC/PKCS5Padding", androidOverride); + + cipher.init(opmode, new SecretKeySpec(key, "AES"), new IvParameterSpec(IV)); + + return cipher; + } + + throw new NoSuchAlgorithmException("UUID unrecognized."); + } +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/NativeAESCipherSpi.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/NativeAESCipherSpi.java new file mode 100644 index 00000000..a73a75e1 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/NativeAESCipherSpi.java @@ -0,0 +1,319 @@ +/* + * Copyright 2009 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.crypto; + +import java.lang.ref.PhantomReference; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidParameterSpecException; +import java.util.HashMap; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.CipherSpi; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.IvParameterSpec; + +import android.util.Log; + +public class NativeAESCipherSpi extends CipherSpi { + + private static boolean mIsStaticInit = false; + private static HashMap, Long> mCleanup = new HashMap, Long>(); + private static ReferenceQueue mQueue = new ReferenceQueue(); + + private final int AES_BLOCK_SIZE = 16; + private byte[] mIV; + + private boolean mIsInited = false; + private boolean mEncrypting = false; + private long mCtxPtr; + + private boolean mPadding = false; + + private static void staticInit() { + mIsStaticInit = true; + + // Start the cipher context cleanup thread to run forever + (new Thread(new Cleanup())).start(); + } + + private static void addToCleanupQueue(NativeAESCipherSpi ref, long ptr) { + Log.d("KeepassDroid", "queued cipher context: " + ptr); + mCleanup.put(new PhantomReference(ref, mQueue), ptr); + } + + /** Work with the garbage collector to clean up openssl memory when the cipher + * context is garbage collected. + * @author bpellin + * + */ + private static class Cleanup implements Runnable { + + public void run() { + while (true) { + try { + Reference ref = mQueue.remove(); + + long ctx = mCleanup.remove(ref); + nCleanup(ctx); + Log.d("KeePassDroid", "Cleaned up cipher context: " + ctx); + + } catch (InterruptedException e) { + // Do nothing, but resume looping if mQueue.remove is interrupted + } + } + } + + } + + private static native void nCleanup(long ctxPtr); + + public NativeAESCipherSpi() { + if ( ! mIsStaticInit ) { + staticInit(); + } + } + + @Override + protected byte[] engineDoFinal(byte[] input, int inputOffset, int inputLen) + throws IllegalBlockSizeException, BadPaddingException { + int maxSize = engineGetOutputSize(inputLen); + byte[] output = new byte[maxSize]; + + int finalSize; + + try { + finalSize = doFinal(input, inputOffset, inputLen, output, 0); + } catch (ShortBufferException e) { + // This shouldn't be possible rethrow as RuntimeException + throw new RuntimeException("Short buffer exception shouldn't be possible from here."); + } + + if ( maxSize == finalSize ) { + return output; + } else { + // TODO: Special doFinal to avoid this copy + byte[] exact = new byte[finalSize]; + System.arraycopy(output, 0, exact, 0, finalSize); + return exact; + } + } + + @Override + protected int engineDoFinal(byte[] input, int inputOffset, int inputLen, + byte[] output, int outputOffset) throws ShortBufferException, + IllegalBlockSizeException, BadPaddingException { + + int result = doFinal(input, inputOffset, inputLen, output, outputOffset); + + if ( result == -1 ) { + throw new ShortBufferException(); + } + + return result; + } + + private int doFinal(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) + throws ShortBufferException, IllegalBlockSizeException, BadPaddingException { + + int outputSize = engineGetOutputSize(inputLen); + + int updateAmt; + if (input != null && inputLen > 0) { + updateAmt = nUpdate(mCtxPtr, input, inputOffset, inputLen, output, outputOffset, outputSize); + } else { + updateAmt = 0; + } + + int finalAmt = nFinal(mCtxPtr, mPadding, output, outputOffset + updateAmt, outputSize - updateAmt); + + int out = updateAmt + finalAmt; + + + return out; + } + + private native int nFinal(long ctxPtr, boolean usePadding, byte[] output, int outputOffest, int outputSize) + throws ShortBufferException, IllegalBlockSizeException, BadPaddingException; + + @Override + protected int engineGetBlockSize() { + return AES_BLOCK_SIZE; + } + + @Override + protected byte[] engineGetIV() { + return mIV.clone(); + } + + @Override + protected int engineGetOutputSize(int inputLen) { + return inputLen + nGetCacheSize(mCtxPtr) + AES_BLOCK_SIZE; + } + + private native int nGetCacheSize(long ctxPtr); + + @Override + protected AlgorithmParameters engineGetParameters() { + // TODO Auto-generated method stub + return null; + } + + @Override + protected void engineInit(int opmode, Key key, SecureRandom random) + throws InvalidKeyException { + + byte[] ivArray = new byte[16]; + random.nextBytes(ivArray); + + init(opmode, key, new IvParameterSpec(ivArray)); + } + + @Override + protected void engineInit(int opmode, Key key, + AlgorithmParameterSpec params, SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException { + + IvParameterSpec ivparam; + + if ( params instanceof IvParameterSpec ) { + ivparam = (IvParameterSpec) params; + } else { + throw new InvalidAlgorithmParameterException("params must be an IvParameterSpec."); + } + + init(opmode, key, ivparam); + } + + + @Override + protected void engineInit(int opmode, Key key, AlgorithmParameters params, + SecureRandom random) throws InvalidKeyException, + InvalidAlgorithmParameterException { + + try { + engineInit(opmode, key, params.getParameterSpec(AlgorithmParameterSpec.class), random); + } catch (InvalidParameterSpecException e) { + throw new InvalidAlgorithmParameterException(e); + } + + } + + private void init(int opmode, Key key, IvParameterSpec params) { + if ( mIsInited ) { + // Do not allow multiple inits + assert(true); + throw new RuntimeException("Don't allow multiple inits"); + } else { + NativeLib.init(); + mIsInited = true; + } + + mIV = params.getIV(); + mEncrypting = opmode == Cipher.ENCRYPT_MODE; + mCtxPtr = nInit(mEncrypting, key.getEncoded(), mIV); + addToCleanupQueue(this, mCtxPtr); + } + + private native long nInit(boolean encrypting, byte[] key, byte[] iv); + + @Override + protected void engineSetMode(String mode) throws NoSuchAlgorithmException { + if ( ! mode.equals("CBC") ) { + throw new NoSuchAlgorithmException("This only supports CBC mode"); + } + } + + @Override + protected void engineSetPadding(String padding) + throws NoSuchPaddingException { + + if ( ! mIsInited ) { + NativeLib.init(); + } + + if ( padding.length() == 0 ) { + return; + } + + if ( ! padding.equals("PKCS5Padding") ) { + throw new NoSuchPaddingException("Only supports PKCS5Padding."); + } + + mPadding = true; + + } + + @Override + protected byte[] engineUpdate(byte[] input, int inputOffset, int inputLen) { + int maxSize = engineGetOutputSize(inputLen); + byte output[] = new byte[maxSize]; + + int updateSize = update(input, inputOffset, inputLen, output, 0); + + if ( updateSize == maxSize ) { + return output; + } else { + // TODO: We could optimize update for this case to avoid this extra copy + byte[] exact = new byte[updateSize]; + System.arraycopy(output, 0, exact, 0, updateSize); + return exact; + } + + } + + @Override + protected int engineUpdate(byte[] input, int inputOffset, int inputLen, + byte[] output, int outputOffset) throws ShortBufferException { + + int result = update(input, inputOffset, inputLen, output, outputOffset); + + if ( result == -1 ) { + throw new ShortBufferException("Insufficient buffer."); + } + + return result; + + } + + int update(byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset) { + int outputSize = engineGetOutputSize(inputLen); + + int out = nUpdate(mCtxPtr, input, inputOffset, inputLen, output, outputOffset, outputSize); + + + return out; + + + } + + private native int nUpdate(long ctxPtr, byte[] input, int inputOffset, int inputLen, byte[] output, int outputOffset, int outputSize); + +} \ No newline at end of file diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/NativeLib.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/NativeLib.java new file mode 100644 index 00000000..be228a6e --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/NativeLib.java @@ -0,0 +1,45 @@ +/* + * Copyright 2009 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.crypto; + +public class NativeLib { + private static boolean isLoaded = false; + private static boolean loadSuccess = false; + + public static boolean loaded() { + return init(); + } + + public static boolean init() { + if ( ! isLoaded ) { + try { + System.loadLibrary("final-key"); + } catch ( UnsatisfiedLinkError e) { + return false; + } + isLoaded = true; + loadSuccess = true; + } + + return loadSuccess; + + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/PwStreamCipherFactory.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/PwStreamCipherFactory.java new file mode 100644 index 00000000..1284f28d --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/PwStreamCipherFactory.java @@ -0,0 +1,65 @@ +/* + * Copyright 2009 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.crypto; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import org.bouncycastle.crypto.StreamCipher; +import org.bouncycastle.crypto.engines.Salsa20Engine; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; + +import com.keepassdroid.database.CrsAlgorithm; + +public class PwStreamCipherFactory { + public static StreamCipher getInstance(CrsAlgorithm alg, byte[] key) { + if ( alg == CrsAlgorithm.Salsa20 ) { + return getSalsa20(key); + + } else { + return null; + } + } + + + private static final byte[] SALSA_IV = new byte[]{ (byte)0xE8, 0x30, 0x09, 0x4B, + (byte)0x97, 0x20, 0x5D, 0x2A }; + + private static StreamCipher getSalsa20(byte[] key) { + // Build stream cipher key + MessageDigest md; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + throw new RuntimeException("SHA 256 not supported"); + } + byte[] key32 = md.digest(key); + + KeyParameter keyParam = new KeyParameter(key32); + ParametersWithIV ivParam = new ParametersWithIV(keyParam, SALSA_IV); + + StreamCipher cipher = new Salsa20Engine(); + cipher.init(true, ivParam); + + return cipher; + } +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/finalkey/AndroidFinalKey.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/finalkey/AndroidFinalKey.java new file mode 100644 index 00000000..50395dc8 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/finalkey/AndroidFinalKey.java @@ -0,0 +1,78 @@ +/* + * Copyright 2009 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.crypto.finalkey; + +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.SecretKeySpec; + +public class AndroidFinalKey extends FinalKey { + + @Override + public byte[] transformMasterKey(byte[] pKeySeed, byte[] pKey, int rounds) throws IOException { + Cipher cipher; + try { + cipher = Cipher.getInstance("AES/ECB/NoPadding"); + } catch (NoSuchAlgorithmException e) { + throw new IOException("NoSuchAlgorithm: " + e.getMessage()); + } catch (NoSuchPaddingException e) { + throw new IOException("NoSuchPadding: " + e.getMessage()); + } + + try { + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(pKeySeed, "AES")); + } catch (InvalidKeyException e) { + throw new IOException("InvalidPasswordException: " + e.getMessage()); + } + + // Encrypt key rounds times + byte[] newKey = new byte[pKey.length]; + System.arraycopy(pKey, 0, newKey, 0, pKey.length); + byte[] destKey = new byte[pKey.length]; + for (int i = 0; i < rounds; i++) { + try { + cipher.update(newKey, 0, newKey.length, destKey, 0); + System.arraycopy(destKey, 0, newKey, 0, newKey.length); + + } catch (ShortBufferException e) { + throw new IOException("Short buffer: " + e.getMessage()); + } + } + + // Hash the key + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + assert true; + throw new IOException("SHA-256 not implemented here: " + e.getMessage()); + } + + md.update(newKey); + return md.digest(); + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/finalkey/FinalKey.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/finalkey/FinalKey.java new file mode 100644 index 00000000..df299a14 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/finalkey/FinalKey.java @@ -0,0 +1,26 @@ +/* + * Copyright 2009 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.crypto.finalkey; + +import java.io.IOException; + +public abstract class FinalKey { + public abstract byte[] transformMasterKey(byte[] seed, byte[] key, int rounds) throws IOException; +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/finalkey/FinalKeyFactory.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/finalkey/FinalKeyFactory.java new file mode 100644 index 00000000..548bd5f9 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/finalkey/FinalKeyFactory.java @@ -0,0 +1,38 @@ +/* + * Copyright 2009-2013 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.crypto.finalkey; + +import com.keepassdroid.crypto.CipherFactory; + +public class FinalKeyFactory { + public static FinalKey createFinalKey() { + return createFinalKey(false); + } + + public static FinalKey createFinalKey(boolean androidOverride) { + // Prefer the native final key implementation + if ( !CipherFactory.deviceBlacklisted() && !androidOverride && NativeFinalKey.availble() ) { + return new NativeFinalKey(); + } else { + // Fall back on the android crypto implementation + return new AndroidFinalKey(); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/finalkey/NativeFinalKey.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/finalkey/NativeFinalKey.java new file mode 100644 index 00000000..ce598295 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/crypto/finalkey/NativeFinalKey.java @@ -0,0 +1,55 @@ +/* + * Copyright 2009 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.crypto.finalkey; + +import java.io.IOException; + +import com.keepassdroid.crypto.NativeLib; + + +public class NativeFinalKey extends FinalKey { + + public static boolean availble() { + return NativeLib.init(); + } + + @Override + public byte[] transformMasterKey(byte[] seed, byte[] key, int rounds) throws IOException { + NativeLib.init(); + + return nTransformMasterKey(seed, key, rounds); + + } + + private static native byte[] nTransformMasterKey(byte[] seed, byte[] key, int rounds); + + // For testing + /* + public static byte[] reflect(byte[] key) { + NativeLib.init(); + + return nativeReflect(key); + } + + private static native byte[] nativeReflect(byte[] key); + */ + + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/CrsAlgorithm.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/CrsAlgorithm.java new file mode 100644 index 00000000..9dddc212 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/CrsAlgorithm.java @@ -0,0 +1,45 @@ +/* + * Copyright 2010 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database; + +public enum CrsAlgorithm { + + Null(0), + ArcFourVariant(1), + Salsa20(2); + + public static final int count = 3; + public final int id; + + private CrsAlgorithm(int num) { + id = num; + } + + public static CrsAlgorithm fromId(int num) { + for ( CrsAlgorithm e : CrsAlgorithm.values() ) { + if ( e.id == num ) { + return e; + } + } + + return null; + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/ITimeLogger.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/ITimeLogger.java new file mode 100644 index 00000000..2b62c861 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/ITimeLogger.java @@ -0,0 +1,46 @@ +/* + * Copyright 2010 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database; + +import java.util.Date; + +public interface ITimeLogger { + Date getLastModificationTime(); + void setLastModificationTime(Date date); + + Date getCreationTime(); + void setCreationTime(Date date); + + Date getLastAccessTime(); + void setLastAccessTime(Date date); + + Date getExpiryTime(); + void setExpiryTime(Date date); + + boolean expires(); + void setExpires(boolean exp); + + long getUsageCount(); + void setUsageCount(long count); + + Date getLocationChanged(); + void setLocationChanged(Date date); + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwCompressionAlgorithm.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwCompressionAlgorithm.java new file mode 100644 index 00000000..676b9737 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwCompressionAlgorithm.java @@ -0,0 +1,47 @@ +/* + * Copyright 2010 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database; + +public enum PwCompressionAlgorithm { + + None(0), + Gzip(1); + + // Note: We can get away with using int's to store unsigned 32-bit ints + // since we won't do arithmetic on these values (also unlikely to + // reach negative ids). + public final int id; + public static final int count = 2; + + private PwCompressionAlgorithm(int num) { + id = num; + } + + public static PwCompressionAlgorithm fromId(int num) { + for ( PwCompressionAlgorithm e : PwCompressionAlgorithm.values() ) { + if ( e.id == num ) { + return e; + } + } + + return null; + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwDatabaseV3.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwDatabaseV3.java new file mode 100644 index 00000000..c069a8b3 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwDatabaseV3.java @@ -0,0 +1,607 @@ +/* + * Copyright 2009-2011 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + * + +Derived from + +KeePass for J2ME + +Copyright 2007 Naomaru Itoi + +This file was derived from + +Java clone of KeePass - A KeePass file viewer for Java +Copyright 2006 Bill Zwicky + +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 + +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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package com.keepassdroid.database; + +// Java +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.security.DigestOutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import com.keepassdroid.crypto.finalkey.FinalKey; +import com.keepassdroid.crypto.finalkey.FinalKeyFactory; +import com.keepassdroid.database.exception.InvalidKeyFileException; +import com.keepassdroid.database.exception.KeyFileEmptyException; +import com.keepassdroid.stream.NullOutputStream; + +/** + * @author Naomaru Itoi + * @author Bill Zwicky + * @author Dominik Reichl + */ +public class PwDatabaseV3 { + // Constants + // private static final int PWM_SESSION_KEY_SIZE = 12; + + + public byte masterKey[] = new byte[32]; + public byte[] finalKey; + public String name = "KeePass database"; + public PwGroupV3 rootGroup; + public PwIconFactory iconFactory = new PwIconFactory(); + //public Map groupList = new HashMap(); + //public Map entryList = new HashMap(); + + + // all entries + public List entries = new ArrayList(); + // all groups + public List groups = new ArrayList(); + // Algorithm used to encrypt the database + public PwEncryptionAlgorithm algorithm; + public int numKeyEncRounds; + + + + public void makeFinalKey(byte[] masterSeed, byte[] masterSeed2, int numRounds) throws IOException { + + // Write checksum Checksum + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new IOException("SHA-256 not implemented here."); + } + NullOutputStream nos = new NullOutputStream(); + DigestOutputStream dos = new DigestOutputStream(nos, md); + + byte[] transformedMasterKey = transformMasterKey(masterSeed2, masterKey, numRounds); + dos.write(masterSeed); + dos.write(transformedMasterKey); + + finalKey = md.digest(); + } + + /** + * Encrypt the master key a few times to make brute-force key-search harder + * @throws IOException + */ + private static byte[] transformMasterKey( byte[] pKeySeed, byte[] pKey, int rounds ) throws IOException + { + FinalKey key = FinalKeyFactory.createFinalKey(); + + return key.transformMasterKey(pKeySeed, pKey, rounds); + } + + + + public void setMasterKey(String key, String keyFileName) + throws InvalidKeyFileException, IOException { + assert( key != null && keyFileName != null ); + + masterKey = getMasterKey(key, keyFileName); + } + + protected byte[] getCompositeKey(String key, String keyFileName) + throws InvalidKeyFileException, IOException { + assert(key != null && keyFileName != null); + + byte[] fileKey = getFileKey(keyFileName); + + byte[] passwordKey = getPasswordKey(key); + + MessageDigest md; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new IOException("SHA-256 not supported"); + } + + md.update(passwordKey); + + return md.digest(fileKey); + } + + protected byte[] getFileKey(String fileName) + throws InvalidKeyFileException, IOException { + assert(fileName != null); + + File keyfile = new File(fileName); + + if ( ! keyfile.exists() ) { + throw new InvalidKeyFileException(); + } + + byte[] key = loadXmlKeyFile(fileName); + if ( key != null ) { + return key; + } + + FileInputStream fis; + try { + fis = new FileInputStream(keyfile); + } catch (FileNotFoundException e) { + throw new InvalidKeyFileException(); + } + + BufferedInputStream bis = new BufferedInputStream(fis, 64); + + long fileSize = keyfile.length(); + if ( fileSize == 0 ) { + throw new KeyFileEmptyException(); + } else if ( fileSize == 32 ) { + byte[] outputKey = new byte[32]; + if ( bis.read(outputKey, 0, 32) != 32 ) { + throw new IOException("Error reading key."); + } + + return outputKey; + } else if ( fileSize == 64 ) { + byte[] hex = new byte[64]; + + bis.mark(64); + if ( bis.read(hex, 0, 64) != 64 ) { + throw new IOException("Error reading key."); + } + + try { + return hexStringToByteArray(new String(hex)); + } catch (IndexOutOfBoundsException e) { + // Key is not base 64, treat it as binary data + bis.reset(); + } + } + + MessageDigest md; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new IOException("SHA-256 not supported"); + } + //SHA256Digest md = new SHA256Digest(); + byte[] buffer = new byte[2048]; + int offset = 0; + + try { + while (true) { + int bytesRead = bis.read(buffer, 0, 2048); + if ( bytesRead == -1 ) break; // End of file + + md.update(buffer, 0, bytesRead); + offset += bytesRead; + + } + } catch (Exception e) { + System.out.println(e.toString()); + } + + return md.digest(); + } + + + public static byte[] hexStringToByteArray(String s) { + int len = s.length(); + byte[] data = new byte[len / 2]; + for (int i = 0; i < len; i += 2) { + data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) + + Character.digit(s.charAt(i+1), 16)); + } + return data; + } + + protected byte[] getPasswordKey(String key, String encoding) throws IOException { + assert(key!=null); + + if ( key.length() == 0 ) + throw new IllegalArgumentException( "Key cannot be empty." ); + + MessageDigest md; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new IOException("SHA-256 not supported"); + } + + byte[] bKey; + try { + bKey = key.getBytes(encoding); + } catch (UnsupportedEncodingException e) { + assert false; + bKey = key.getBytes(); + } + md.update(bKey, 0, bKey.length ); + + return md.digest(); + } + + public void super_addGroupTo(PwGroupV3 newGroup, PwGroupV3 parent) { + // Add group to parent group + if ( parent == null ) { + parent = rootGroup; + } + + parent.childGroups.add(newGroup); + newGroup.setParent(parent); + //groupList.put(newGroup.getId(), newGroup); + + parent.touch(true, true); + } + + public void super_removeGroupFrom(PwGroupV3 remove, PwGroupV3 parent) { + // Remove group from parent group + parent.childGroups.remove(remove); + + //groupList.remove(remove.getId()); + } + + public void super_addEntryTo(PwEntryV3 newEntry, PwGroupV3 parent) { + // Add entry to parent + if (parent != null) { + parent.childEntries.add(newEntry); + } + newEntry.setParent(parent); + + //entryList.put(newEntry.getUUID(), newEntry); + } + + public void super_removeEntryFrom(PwEntryV3 remove, PwGroupV3 parent) { + // Remove entry for parent + if (parent != null) { + parent.childEntries.remove(remove); + } + //entryList.remove(remove.getUUID()); + } + + + /** + * Determine if an id number is already in use + * + * @param id + * ID number to check for + * @return True if the ID is used, false otherwise + */ + protected boolean isGroupIdUsed(PwGroupId id) { + List groups = getGroups(); + + for (int i = 0; i < groups.size(); i++) { + PwGroupV3 group =groups.get(i); + if (group.getId().equals(id)) { + return true; + } + } + + return false; + } + + + + public boolean canRecycle(PwGroupV3 group) { + return false; + } + + public boolean canRecycle(PwEntryV3 entry) { + return false; + } + + public void recycle(PwEntryV3 entry) { + // Assume calls to this are protected by calling inRecyleBin + throw new RuntimeException("Call not valid for .kdb databases."); + } + + public void undoRecycle(PwEntryV3 entry, PwGroupV3 origParent) { + throw new RuntimeException("Call not valid for .kdb databases."); + } + + public void deleteEntry(PwEntryV3 entry) { + PwGroupV3 parent = entry.getParent(); + removeEntryFrom(entry, parent); + parent.touch(false, true); + + } + + public void undoDeleteEntry(PwEntryV3 entry, PwGroupV3 origParent) { + addEntryTo(entry, origParent); + } + + public PwGroupV3 getRecycleBin() { + return null; + } + + public boolean super_isGroupSearchable(PwGroupV3 group, boolean omitBackup) { + return group != null; + } + + + public PwEncryptionAlgorithm getEncAlgorithm() { + return algorithm; + } + + public int getNumKeyEncRecords() { + return numKeyEncRounds; + } + + public List getGroups() { + return groups; + } + + public List getEntries() { + return entries; + } + + public void setGroups(List grp) { + groups = grp; + } + + public List getGrpRoots() { + int target = 0; + List kids = new ArrayList(); + for (int i = 0; i < groups.size(); i++) { + PwGroupV3 grp = (PwGroupV3) groups.get(i); + if (grp.level == target) + kids.add(grp); + } + return kids; + } + + public int getRootGroupId() { + for (int i = 0; i < groups.size(); i++) { + PwGroupV3 grp = (PwGroupV3) groups.get(i); + if (grp.level == 0) { + return grp.groupId; + } + } + + return -1; + } + + public List getGrpChildren(PwGroupV3 parent) { + int idx = groups.indexOf(parent); + int target = parent.level + 1; + List kids = new ArrayList(); + while (++idx < groups.size()) { + PwGroupV3 grp = (PwGroupV3) groups.get(idx); + if (grp.level < target) + break; + else if (grp.level == target) + kids.add(grp); + } + return kids; + } + + public List getEntries(PwGroupV3 parent) { + List kids = new ArrayList(); + /* + * for( Iterator i = entries.iterator(); i.hasNext(); ) { PwEntryV3 ent + * = (PwEntryV3)i.next(); if( ent.groupId == parent.groupId ) kids.add( + * ent ); } + */ + for (int i = 0; i < entries.size(); i++) { + PwEntryV3 ent = (PwEntryV3) entries.get(i); + if (ent.groupId == parent.groupId) + kids.add(ent); + } + return kids; + } + + public String toString() { + return name; + } + + public void constructTree(PwGroupV3 currentGroup) { + // I'm in root + if (currentGroup == null) { + PwGroupV3 root = new PwGroupV3(); + rootGroup = root; + + List rootChildGroups = getGrpRoots(); + root.setGroups(rootChildGroups); + root.childEntries = new ArrayList(); + root.level = -1; + for (int i = 0; i < rootChildGroups.size(); i++) { + PwGroupV3 grp = (PwGroupV3) rootChildGroups.get(i); + grp.parent = root; + constructTree(grp); + } + return; + } + + // I'm in non-root + // get child groups + currentGroup.setGroups(getGrpChildren(currentGroup)); + currentGroup.childEntries = getEntries(currentGroup); + + // set parent in child entries + for (int i = 0; i < currentGroup.childEntries.size(); i++) { + PwEntryV3 entry = (PwEntryV3) currentGroup.childEntries.get(i); + entry.parent = currentGroup; + } + // recursively construct child groups + for (int i = 0; i < currentGroup.childGroups.size(); i++) { + PwGroupV3 grp = (PwGroupV3) currentGroup.childGroups.get(i); + grp.parent = currentGroup; + constructTree((PwGroupV3) currentGroup.childGroups.get(i)); + } + return; + } + + /* + public void removeGroup(PwGroupV3 group) { + group.parent.childGroups.remove(group); + groups.remove(group); + } + */ + + public PwGroupIdV3 newGroupId() { + PwGroupIdV3 newId = new PwGroupIdV3(0); + + Random random = new Random(); + + while (true) { + newId = new PwGroupIdV3(random.nextInt()); + + if (!isGroupIdUsed(newId)) break; + } + + return newId; + } + + public byte[] getMasterKey(String key, String keyFileName) + throws InvalidKeyFileException, IOException { + assert (key != null && keyFileName != null); + + if (key.length() > 0 && keyFileName.length() > 0) { + return getCompositeKey(key, keyFileName); + } else if (key.length() > 0) { + return getPasswordKey(key); + } else if (keyFileName.length() > 0) { + return getFileKey(keyFileName); + } else { + throw new IllegalArgumentException("Key cannot be empty."); + } + + } + + public byte[] getPasswordKey(String key) throws IOException { + return getPasswordKey(key, "ISO-8859-1"); + } + + protected byte[] loadXmlKeyFile(String fileName) { + return null; + } + + + + public long getNumRounds() { + return numKeyEncRounds; + } + + public void setNumRounds(long rounds) throws NumberFormatException { + if (rounds > Integer.MAX_VALUE || rounds < Integer.MIN_VALUE) { + throw new NumberFormatException(); + } + + numKeyEncRounds = (int) rounds; + } + + public boolean appSettingsEnabled() { + return true; + } + + + public void addEntryTo(PwEntryV3 newEntry, PwGroupV3 parent) { + super_addEntryTo(newEntry, parent); + + // Add entry to root entries + entries.add(newEntry); + + } + + + public void addGroupTo(PwGroupV3 newGroup, PwGroupV3 parent) { + super_addGroupTo(newGroup, parent); + + // Add group to root groups + groups.add(newGroup); + + } + + + public void removeEntryFrom(PwEntryV3 remove, PwGroupV3 parent) { + super_removeEntryFrom(remove, parent); + + // Remove entry from root entry + entries.remove(remove); + } + + + public void removeGroupFrom(PwGroupV3 remove, PwGroupV3 parent) { + super_removeGroupFrom(remove, parent); + + // Remove group from root entry + groups.remove(remove); + } + + public PwGroupV3 createGroup() { + return new PwGroupV3(); + } + + // TODO: This could still be refactored cleaner + public void copyEncrypted(byte[] buf, int offset, int size) { + // No-op + } + + // TODO: This could still be refactored cleaner + public void copyHeader(PwDbHeaderV3 header) { + // No-op + } + public boolean isBackup(PwGroupV3 group) { + PwGroupV3 g = (PwGroupV3) group; + while (g != null) { + if (g.level == 0 && g.name.equalsIgnoreCase("Backup")) { + return true; + } + + g = g.parent; + } + + return false; + } + + public boolean isGroupSearchable(PwGroupV3 group, boolean omitBackup) { + if (!super_isGroupSearchable(group, omitBackup)) { + return false; + } + + return !(omitBackup && isBackup(group)); + } +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwDate.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwDate.java new file mode 100644 index 00000000..74f979b5 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwDate.java @@ -0,0 +1,221 @@ +/* + * Copyright 2009-2013 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database; + +import java.util.Arrays; +import java.util.Calendar; +import java.util.Date; + +import com.keepassdroid.utils.Types; + +/** Converting from the C Date format to the Java data format is + * expensive when done for every record at once. I use this class to + * allow lazy conversions between the formats. + * @author bpellin + * + */ +public class PwDate implements Cloneable { + + private static final int DATE_SIZE = 5; + + private boolean cDateBuilt = false; + private boolean jDateBuilt = false; + + private Date jDate; + private byte[] cDate; + + public PwDate(byte[] buf, int offset) { + cDate = new byte[DATE_SIZE]; + System.arraycopy(buf, offset, cDate, 0, DATE_SIZE); + cDateBuilt = true; + } + + public PwDate(Date date) { + jDate = date; + jDateBuilt = true; + } + + public PwDate(long millis) { + jDate = new Date(millis); + jDateBuilt = true; + } + + private PwDate() { + + } + + @Override + public Object clone() { + PwDate copy = new PwDate(); + + if ( cDateBuilt ) { + byte[] newC = new byte[DATE_SIZE]; + System.arraycopy(cDate, 0, newC, 0, DATE_SIZE); + copy.cDate = newC; + copy.cDateBuilt = true; + } + + if ( jDateBuilt ) { + copy.jDate = (Date) jDate.clone(); + copy.jDateBuilt = true; + } + + return copy; + } + + + + public Date getJDate() { + if ( ! jDateBuilt ) { + jDate = readTime(cDate, 0, getCalendar()); + jDateBuilt = true; + } + + return jDate; + } + + public byte[] getCDate() { + if ( ! cDateBuilt ) { + cDate = writeTime(jDate, getCalendar()); + cDateBuilt = true; + } + + return cDate; + } + + private static Calendar calendar = null; + private static Calendar getCalendar() { + if ( calendar == null ) { + calendar = Calendar.getInstance(); + } + + return calendar; + } + + + /** + * Unpack date from 5 byte format. The five bytes at 'offset' are unpacked + * to a java.util.Date instance. + */ + public static Date readTime(byte[] buf, int offset, Calendar time) { + int dw1 = Types.readUByte(buf, offset); + int dw2 = Types.readUByte(buf, offset + 1); + int dw3 = Types.readUByte(buf, offset + 2); + int dw4 = Types.readUByte(buf, offset + 3); + int dw5 = Types.readUByte(buf, offset + 4); + + // Unpack 5 byte structure to date and time + int year = (dw1 << 6) | (dw2 >> 2); + int month = ((dw2 & 0x00000003) << 2) | (dw3 >> 6); + + int day = (dw3 >> 1) & 0x0000001F; + int hour = ((dw3 & 0x00000001) << 4) | (dw4 >> 4); + int minute = ((dw4 & 0x0000000F) << 2) | (dw5 >> 6); + int second = dw5 & 0x0000003F; + + if (time == null) { + time = Calendar.getInstance(); + } + // File format is a 1 based month, java Calendar uses a zero based month + // File format is a 1 based day, java Calendar uses a 1 based day + time.set(year, month - 1, day, hour, minute, second); + + return time.getTime(); + + } + + public static byte[] writeTime(Date date) { + return writeTime(date, null); + } + + public static byte[] writeTime(Date date, Calendar cal) { + if (date == null) { + return null; + } + + byte[] buf = new byte[5]; + if (cal == null) { + cal = Calendar.getInstance(); + } + cal.setTime(date); + + int year = cal.get(Calendar.YEAR); + // File format is a 1 based month, java Calendar uses a zero based month + int month = cal.get(Calendar.MONTH) + 1; + // File format is a 0 based day, java Calendar uses a 1 based day + int day = cal.get(Calendar.DAY_OF_MONTH) - 1; + int hour = cal.get(Calendar.HOUR_OF_DAY); + int minute = cal.get(Calendar.MINUTE); + int second = cal.get(Calendar.SECOND); + + buf[0] = Types.writeUByte(((year >> 6) & 0x0000003F)); + buf[1] = Types.writeUByte(((year & 0x0000003F) << 2) + | ((month >> 2) & 0x00000003)); + buf[2] = (byte) (((month & 0x00000003) << 6) + | ((day & 0x0000001F) << 1) | ((hour >> 4) & 0x00000001)); + buf[3] = (byte) (((hour & 0x0000000F) << 4) | ((minute >> 2) & 0x0000000F)); + buf[4] = (byte) (((minute & 0x00000003) << 6) | (second & 0x0000003F)); + + return buf; + } + + @Override + public boolean equals(Object o) { + if ( this == o ) { + return true; + } + if ( o == null ) { + return false; + } + if ( getClass() != o.getClass() ) { + return false; + } + + PwDate date = (PwDate) o; + if ( cDateBuilt && date.cDateBuilt ) { + return Arrays.equals(cDate, date.cDate); + } else if ( jDateBuilt && date.jDateBuilt ) { + return IsSameDate(jDate, date.jDate); + } else if ( cDateBuilt && date.jDateBuilt ) { + return Arrays.equals(date.getCDate(), cDate); + } else { + return IsSameDate(date.getJDate(), jDate); + } + } + + public static boolean IsSameDate(Date d1, Date d2) { + Calendar cal1 = Calendar.getInstance(); + cal1.setTime(d1); + cal1.set(Calendar.MILLISECOND, 0); + + Calendar cal2 = Calendar.getInstance(); + cal2.setTime(d2); + cal2.set(Calendar.MILLISECOND, 0); + + return (cal1.get(Calendar.YEAR) == cal2.get(Calendar.YEAR)) && + (cal1.get(Calendar.MONTH) == cal2.get(Calendar.MONTH)) && + (cal1.get(Calendar.DAY_OF_MONTH) == cal2.get(Calendar.DAY_OF_MONTH)) && + (cal1.get(Calendar.HOUR) == cal2.get(Calendar.HOUR)) && + (cal1.get(Calendar.MINUTE) == cal2.get(Calendar.MINUTE)) && + (cal1.get(Calendar.SECOND) == cal2.get(Calendar.SECOND)); + + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwDbHeader.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwDbHeader.java new file mode 100644 index 00000000..4015dc06 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwDbHeader.java @@ -0,0 +1,35 @@ +/* + * Copyright 2009 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database; + +public abstract class PwDbHeader { + + public static final int PWM_DBSIG_1 = 0x9AA2D903; + + /** Seed that gets hashed with the userkey to form the final key */ + public byte masterSeed[]; + + /** Used for the dwKeyEncRounds AES transformations */ + public byte transformSeed[] = new byte[32]; + + /** IV used for content encryption */ + public byte encryptionIV[] = new byte[16]; + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwDbHeaderFactory.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwDbHeaderFactory.java new file mode 100644 index 00000000..43aa3d92 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwDbHeaderFactory.java @@ -0,0 +1,27 @@ +/* + * Copyright 2011 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database; + +public class PwDbHeaderFactory { + public static PwDbHeader getInstance(PwDatabaseV3 db) { + return new PwDbHeaderV3(); + + } +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwDbHeaderV3.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwDbHeaderV3.java new file mode 100644 index 00000000..904a0305 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwDbHeaderV3.java @@ -0,0 +1,133 @@ +/* + * Copyright 2009-2011 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + * + +Derived from + +KeePass for J2ME + + +Copyright 2007 Naomaru Itoi + +This file was derived from + +Java clone of KeePass - A KeePass file viewer for Java +Copyright 2006 Bill Zwicky + +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; version 2 + +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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package com.keepassdroid.database; + +import java.io.IOException; + +import com.keepassdroid.stream.LEDataInputStream; + +public class PwDbHeaderV3 extends PwDbHeader { + + // DB sig from KeePass 1.03 + public static final int DBSIG_2 = 0xB54BFB65; + // DB sig from KeePass 1.03 + public static final int DBVER_DW = 0x00030003; + + public static final int FLAG_SHA2 = 1; + public static final int FLAG_RIJNDAEL = 2; + public static final int FLAG_ARCFOUR = 4; + public static final int FLAG_TWOFISH = 8; + + /** Size of byte buffer needed to hold this struct. */ + public static final int BUF_SIZE = 124; + + + + public int signature1; // = PWM_DBSIG_1 + public int signature2; // = DBSIG_2 + public int flags; + public int version; + + /** Number of groups in the database */ + public int numGroups; + /** Number of entries in the database */ + public int numEntries; + + /** SHA-256 hash of the database, used for integrity check */ + public byte contentsHash[] = new byte[32]; + + public int numKeyEncRounds; + + /** + * Parse given buf, as read from file. + * @param buf + * @throws IOException + */ + public void loadFromFile( byte buf[], int offset ) throws IOException { + signature1 = LEDataInputStream.readInt( buf, offset + 0 ); + signature2 = LEDataInputStream.readInt( buf, offset + 4 ); + flags = LEDataInputStream.readInt( buf, offset + 8 ); + version = LEDataInputStream.readInt( buf, offset + 12 ); + + System.arraycopy( buf, offset + 16, masterSeed, 0, 16 ); + System.arraycopy( buf, offset + 32, encryptionIV, 0, 16 ); + + numGroups = LEDataInputStream.readInt( buf, offset + 48 ); + numEntries = LEDataInputStream.readInt( buf, offset + 52 ); + + System.arraycopy( buf, offset + 56, contentsHash, 0, 32 ); + + System.arraycopy( buf, offset + 88, transformSeed, 0, 32 ); + numKeyEncRounds = LEDataInputStream.readInt( buf, offset + 120 ); + if ( numKeyEncRounds < 0 ) { + // TODO: Really treat this like an unsigned integer + throw new IOException("Does not support more than " + Integer.MAX_VALUE + " rounds."); + } + } + + public PwDbHeaderV3() { + masterSeed = new byte[16]; + } + + public static boolean matchesHeader(int sig1, int sig2) { + return (sig1 == PWM_DBSIG_1) && (sig2 == DBSIG_2); + } + + + /** Determine if the database version is compatible with this application + * @return true, if it is compatible + */ + public boolean matchesVersion() { + return compatibleHeaders(version, DBVER_DW); + } + + public static boolean compatibleHeaders(int one, int two) { + return (one & 0xFFFFFF00) == (two & 0xFFFFFF00); + } + + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwDeletedObject.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwDeletedObject.java new file mode 100644 index 00000000..0e3f743c --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwDeletedObject.java @@ -0,0 +1,70 @@ +/* + * Copyright 2010-2013 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database; + +import java.util.Date; +import java.util.UUID; + +public class PwDeletedObject { + public UUID uuid; + private Date deletionTime; + + public PwDeletedObject() { + + } + + public PwDeletedObject(UUID u) { + this(u, new Date()); + } + + public PwDeletedObject(UUID u, Date d) { + uuid = u; + deletionTime = d; + } + + public Date getDeletionTime() { + if ( deletionTime == null ) { + return new Date(System.currentTimeMillis()); + } + + return deletionTime; + } + + public void setDeletionTime(Date date) { + deletionTime = date; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + else if (o == null) { + return false; + } + else if (!(o instanceof PwDeletedObject)) { + return false; + } + + PwDeletedObject rhs = (PwDeletedObject) o; + + return uuid.equals(rhs.uuid); + } +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwEncryptionAlgorithm.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwEncryptionAlgorithm.java new file mode 100644 index 00000000..72175f78 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwEncryptionAlgorithm.java @@ -0,0 +1,26 @@ +/* + * Copyright 2010 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database; + +public enum PwEncryptionAlgorithm { + + Rjindal, + Twofish; +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwEntryV3.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwEntryV3.java new file mode 100644 index 00000000..fc456444 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwEntryV3.java @@ -0,0 +1,547 @@ +/* + * Copyright 2010-2013 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + +This file was derived from + +Copyright 2007 Naomaru Itoi + +This file was derived from + +Java clone of KeePass - A KeePass file viewer for Java +Copyright 2006 Bill Zwicky + +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; version 2 + +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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package com.keepassdroid.database; + +// PhoneID +import java.io.UnsupportedEncodingException; +import java.util.Calendar; +import java.util.Date; +import java.util.Random; +import java.util.UUID; + +import com.keepassdroid.utils.Types; + + +/** + * Structure containing information about one entry. + * + *
+ * One entry: [FIELDTYPE(FT)][FIELDSIZE(FS)][FIELDDATA(FD)]
+ *            [FT+FS+(FD)][FT+FS+(FD)][FT+FS+(FD)][FT+FS+(FD)][FT+FS+(FD)]...
+ *            
+ * [ 2 bytes] FIELDTYPE
+ * [ 4 bytes] FIELDSIZE, size of FIELDDATA in bytes
+ * [ n bytes] FIELDDATA, n = FIELDSIZE
+ * 
+ * Notes:
+ *  - Strings are stored in UTF-8 encoded form and are null-terminated.
+ *  - FIELDTYPE can be one of the FT_ constants.
+ * 
+ * + * @author Naomaru Itoi + * @author Bill Zwicky + * @author Dominik Reichl + */ +public class PwEntryV3 { + +protected static final String PMS_TAN_ENTRY = ""; + + + + public static PwEntryV3 getInstance(PwGroupV3 parent) { + return PwEntryV3.getInstance(parent, true, true); + } + + public static PwEntryV3 getInstance(PwGroupV3 parent, boolean initId, boolean initDates) { + return new PwEntryV3((PwGroupV3)parent); + + } + + + + + public PwIcon getIcon() { + return icon; + } + + public boolean isTan() { + return getTitle().equals(PMS_TAN_ENTRY) && (getUsername().length() > 0); + } + + public String getDisplayTitle() { + if ( isTan() ) { + return PMS_TAN_ENTRY + " " + getUsername(); + } else { + return getTitle(); + } + } + + + + + public void touch(boolean modified, boolean touchParents) { + Date now = new Date(); + + setLastAccessTime(now); + + if (modified) { + setLastModificationTime(now); + } + + PwGroupV3 parent = getParent(); + if (touchParents && parent != null) { + parent.touch(modified, true); + } + } + + public void touchLocation() { } + + + + public static final Date NEVER_EXPIRE = getNeverExpire(); + public static final Date NEVER_EXPIRE_BUG = getNeverExpireBug(); + public static final Date DEFAULT_DATE = getDefaultDate(); + public static final PwDate PW_NEVER_EXPIRE = new PwDate(NEVER_EXPIRE); + public static final PwDate PW_NEVER_EXPIRE_BUG = new PwDate(NEVER_EXPIRE_BUG); + public static final PwDate DEFAULT_PWDATE = new PwDate(DEFAULT_DATE); + + + /** Size of byte buffer needed to hold this struct. */ + public static final String PMS_ID_BINDESC = "bin-stream"; + public static final String PMS_ID_TITLE = "Meta-Info"; + public static final String PMS_ID_USER = "SYSTEM"; + public static final String PMS_ID_URL = "$"; + + + public PwIconStandard icon = PwIconStandard.FIRST; + + public int groupId; + public String username; + private byte[] password; + private byte[] uuid; + public String title; + public String url; + public String additional; + + + public PwDate tCreation; + public PwDate tLastMod; + public PwDate tLastAccess; + public PwDate tExpire; + + /** A string describing what is in pBinaryData */ + public String binaryDesc; + private byte[] binaryData; + + private static Date getDefaultDate() { + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.YEAR, 2004); + cal.set(Calendar.MONTH, Calendar.JANUARY); + cal.set(Calendar.DAY_OF_MONTH, 1); + cal.set(Calendar.HOUR, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + + return cal.getTime(); + } + + private static Date getNeverExpire() { + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.YEAR, 2999); + cal.set(Calendar.MONTH, 11); + cal.set(Calendar.DAY_OF_MONTH, 28); + cal.set(Calendar.HOUR, 23); + cal.set(Calendar.MINUTE, 59); + cal.set(Calendar.SECOND, 59); + + return cal.getTime(); + } + + /** This date was was accidentally being written + * out when an entry was supposed to be marked as + * expired. We'll use this to silently correct those + * entries. + * @return + */ + private static Date getNeverExpireBug() { + Calendar cal = Calendar.getInstance(); + cal.set(Calendar.YEAR, 2999); + cal.set(Calendar.MONTH, 11); + cal.set(Calendar.DAY_OF_MONTH, 30); + cal.set(Calendar.HOUR, 23); + cal.set(Calendar.MINUTE, 59); + cal.set(Calendar.SECOND, 59); + + return cal.getTime(); + } + + public static boolean IsNever(Date date) { + return PwDate.IsSameDate(NEVER_EXPIRE, date); + } + + // for tree traversing + public PwGroupV3 parent = null; + + + public PwEntryV3() { + super(); + } + + /* + public PwEntryV3(PwEntryV3 source) { + assign(source); + } + */ + + public PwEntryV3(PwGroupV3 p) { + this(p, true, true); + } + + public PwEntryV3(PwGroupV3 p, boolean initId, boolean initDates) { + + parent = p; + groupId = ((PwGroupIdV3)parent.getId()).getId(); + + if (initId) { + Random random = new Random(); + uuid = new byte[16]; + random.nextBytes(uuid); + } + + if (initDates) { + Calendar cal = Calendar.getInstance(); + Date now = cal.getTime(); + tCreation = new PwDate(now); + tLastAccess = new PwDate(now); + tLastMod = new PwDate(now); + tExpire = new PwDate(NEVER_EXPIRE); + } + + } + + /** + * @return the actual password byte array. + */ + public String getPassword() { + if (password == null) { + return ""; + } + + return new String(password); + } + + public byte[] getPasswordBytes() { + return password; + } + + + /** + * fill byte array + */ + private static void fill(byte[] array, byte value) + { + for (int i=0; i. + * + */ +package com.keepassdroid.database; + +public class PwGroupIdV3 extends PwGroupId { + + private int id; + + public PwGroupIdV3(int i) { + id = i; + } + + @Override + public boolean equals(Object compare) { + if ( ! (compare instanceof PwGroupIdV3) ) { + return false; + } + + PwGroupIdV3 cmp = (PwGroupIdV3) compare; + return id == cmp.id; + } + + @Override + public int hashCode() { + Integer i = Integer.valueOf(id); + return i.hashCode(); + } + + public int getId() { + return id; + } + + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwGroupV3.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwGroupV3.java new file mode 100644 index 00000000..4929528f --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwGroupV3.java @@ -0,0 +1,192 @@ +/* + * Copyright 2009 Brian Pellin. + +This file was derived from + +Copyright 2007 Naomaru Itoi + +This file was derived from + +Java clone of KeePass - A KeePass file viewer for Java +Copyright 2006 Bill Zwicky + + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ + +package com.keepassdroid.database; + +import java.security.acl.LastOwnerException; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; + +/** + * @author Brian Pellin + * @author Naomaru Itoi + * @author Bill Zwicky + * @author Dominik Reichl + */ +public class PwGroupV3 { + public PwGroupV3() { + } + + public List childGroups = new ArrayList(); + + public List childEntries = new ArrayList(); + public String name = ""; + public PwIconStandard icon; + + public PwIcon getIcon() { + return icon; + } + + public void super_initNewGroup(String nm, PwGroupId newId) { + setId(newId); + name = nm; + } + + public boolean isContainedIn(PwGroupV3 container) { + PwGroupV3 cur = this; + while (cur != null) { + if (cur == container) { + return true; + } + + cur = cur.getParent(); + } + + return false; + } + + public void touch(boolean modified, boolean touchParents) { + Date now = new Date(); + + setLastAccessTime(now); + + if (modified) { + setLastModificationTime(now); + } + + PwGroupV3 parent = getParent(); + if (touchParents && parent != null) { + parent.touch(modified, true); + } + } + + public String toString() { + return name; + } + + public static final Date NEVER_EXPIRE = PwEntryV3.NEVER_EXPIRE; + + /** Size of byte buffer needed to hold this struct. */ + public static final int BUF_SIZE = 124; + + // for tree traversing + public PwGroupV3 parent = null; + + public int groupId; + + public PwDate tCreation; + public PwDate tLastMod; + public PwDate tLastAccess; + public PwDate tExpire; + + public int level; // short + + /** Used by KeePass internally, don't use */ + public int flags; + + public void setGroups(List groups) { + childGroups = groups; + } + + public PwGroupV3 getParent() { + return parent; + } + + public PwGroupId getId() { + return new PwGroupIdV3(groupId); + } + + public void setId(PwGroupId id) { + PwGroupIdV3 id3 = (PwGroupIdV3) id; + groupId = id3.getId(); + } + + public String getName() { + return name; + } + + public Date getLastMod() { + return tLastMod.getJDate(); + } + + public void setParent(PwGroupV3 prt) { + parent = (PwGroupV3) prt; + level = parent.level + 1; + + } + + public void initNewGroup(String nm, PwGroupId newId) { + super_initNewGroup(nm, newId); + + Date now = Calendar.getInstance().getTime(); + tCreation = new PwDate(now); + tLastAccess = new PwDate(now); + tLastMod = new PwDate(now); + tExpire = new PwDate(PwGroupV3.NEVER_EXPIRE); + + } + + public void populateBlankFields(PwDatabaseV3 db) { + if (icon == null) { + icon = db.iconFactory.getIcon(1); + } + + if (name == null) { + name = ""; + } + + if (tCreation == null) { + tCreation = PwEntryV3.DEFAULT_PWDATE; + } + + if (tLastMod == null) { + tLastMod = PwEntryV3.DEFAULT_PWDATE; + } + + if (tLastAccess == null) { + tLastAccess = PwEntryV3.DEFAULT_PWDATE; + } + + if (tExpire == null) { + tExpire = PwEntryV3.DEFAULT_PWDATE; + } + } + + public void setLastAccessTime(Date date) { + tLastAccess = new PwDate(date); + } + + public void setLastModificationTime(Date date) { + tLastMod = new PwDate(date); + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwIcon.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwIcon.java new file mode 100644 index 00000000..798ad252 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwIcon.java @@ -0,0 +1,12 @@ +package com.keepassdroid.database; + +public abstract class PwIcon { + + public boolean isMetaStreamIcon() { + return false; + } + + public void writeBytes() { + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwIconFactory.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwIconFactory.java new file mode 100644 index 00000000..a440c51e --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwIconFactory.java @@ -0,0 +1,58 @@ +/* + * Copyright 2010-2013 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database; + +import java.util.UUID; + +import org.apache.commons.collections.map.AbstractReferenceMap; +import org.apache.commons.collections.map.ReferenceMap; + +public class PwIconFactory { + /** customIconMap + * Cache for icon drawable. + * Keys: Integer, Values: PwIconStandard + */ + private ReferenceMap cache = new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK); + + /** standardIconMap + * Cache for icon drawable. + * Keys: UUID, Values: PwIconCustom + */ + private ReferenceMap customCache = new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK); + + public PwIconStandard getIcon(int iconId) { + PwIconStandard icon = (PwIconStandard) cache.get(iconId); + + if (icon == null) { + if (iconId == 1) { + icon = PwIconStandard.FIRST; + } + else { + icon = new PwIconStandard(iconId); + } + cache.put(iconId, icon); + } + + return icon; + } + + + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwIconStandard.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwIconStandard.java new file mode 100644 index 00000000..74325048 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/PwIconStandard.java @@ -0,0 +1,59 @@ +/* + * Copyright 2010-2013 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database; + +public class PwIconStandard extends PwIcon { + public final int iconId; + + public static PwIconStandard FIRST = new PwIconStandard(1); + + public static final int TRASH_BIN = 43; + + public PwIconStandard(int iconId) { + this.iconId = iconId; + } + + @Override + public boolean isMetaStreamIcon() { + return iconId == 0; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + iconId; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + PwIconStandard other = (PwIconStandard) obj; + if (iconId != other.iconId) + return false; + return true; + } +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/ArcFourException.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/ArcFourException.java new file mode 100644 index 00000000..3dacb860 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/ArcFourException.java @@ -0,0 +1,29 @@ +/* + * Copyright 2010 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database.exception; + +public class ArcFourException extends InvalidDBException { + + /** + * + */ + private static final long serialVersionUID = 2103983626687861237L; + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InconsistentDBException.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InconsistentDBException.java new file mode 100644 index 00000000..be9bf80a --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InconsistentDBException.java @@ -0,0 +1,30 @@ +/* + * Copyright 2010 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database.exception; + +public class InconsistentDBException extends Exception { + + public InconsistentDBException(String msg) { + super(msg); + } + + private static final long serialVersionUID = 4879502365625912291L; + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InvalidAlgorithmException.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InvalidAlgorithmException.java new file mode 100644 index 00000000..97b783ec --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InvalidAlgorithmException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2012 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database.exception; + +public class InvalidAlgorithmException extends InvalidDBException { + /** + * + */ + private static final long serialVersionUID = 3062682891863487208L; + + public InvalidAlgorithmException() { + super(); + } + +} \ No newline at end of file diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InvalidDBException.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InvalidDBException.java new file mode 100644 index 00000000..4f62f2d9 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InvalidDBException.java @@ -0,0 +1,37 @@ +/* + * Copyright 2009 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database.exception; + +public class InvalidDBException extends Exception { + + public InvalidDBException(String str) { + super(str); + } + + public InvalidDBException() { + super(); + } + + /** + * + */ + private static final long serialVersionUID = 5191964825154190923L; + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InvalidDBSignatureException.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InvalidDBSignatureException.java new file mode 100644 index 00000000..d2ac4aea --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InvalidDBSignatureException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2009 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database.exception; + +public class InvalidDBSignatureException extends InvalidDBException { + /** + * + */ + private static final long serialVersionUID = -5358923878743513758L; + + public InvalidDBSignatureException() { + super(); + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InvalidDBVersionException.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InvalidDBVersionException.java new file mode 100644 index 00000000..b7f6118f --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InvalidDBVersionException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2010 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database.exception; + +public class InvalidDBVersionException extends InvalidDBException { + /** + * + */ + private static final long serialVersionUID = -4260650987856400586L; + + public InvalidDBVersionException() { + super(); + } + +} \ No newline at end of file diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InvalidKeyFileException.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InvalidKeyFileException.java new file mode 100644 index 00000000..11cd1d8b --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InvalidKeyFileException.java @@ -0,0 +1,30 @@ +/* + * Copyright 2009-2012 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */package com.keepassdroid.database.exception; + +public class InvalidKeyFileException extends InvalidDBException { + /** + * + */ + private static final long serialVersionUID = 5540694419562294464L; + + public InvalidKeyFileException() { + super(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InvalidPasswordException.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InvalidPasswordException.java new file mode 100644 index 00000000..69096e53 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/InvalidPasswordException.java @@ -0,0 +1,36 @@ +/* + * Copyright 2009 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database.exception; + +public class InvalidPasswordException extends InvalidDBException { + + /** + * + */ + private static final long serialVersionUID = -8729476180242058319L; + + public InvalidPasswordException(String str) { + super(str); + } + + public InvalidPasswordException() { + super(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/KeyFileEmptyException.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/KeyFileEmptyException.java new file mode 100644 index 00000000..21a7a101 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/KeyFileEmptyException.java @@ -0,0 +1,30 @@ +/* + * Copyright 2012 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */package com.keepassdroid.database.exception; + +public class KeyFileEmptyException extends InvalidKeyFileException { + /** + * + */ + private static final long serialVersionUID = -1630780661204212325L; + + public KeyFileEmptyException() { + super(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/PwDbOutputException.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/PwDbOutputException.java new file mode 100644 index 00000000..71c9dc32 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/PwDbOutputException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2009-2013 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database.exception; + +public class PwDbOutputException extends Exception { + public PwDbOutputException(String string) { + super(string); + } + + public PwDbOutputException(Exception e) { + super(e); + } + + /** + * + */ + private static final long serialVersionUID = 3321212743159473368L; +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/SamsungClipboardException.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/SamsungClipboardException.java new file mode 100644 index 00000000..c686c85b --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/exception/SamsungClipboardException.java @@ -0,0 +1,37 @@ +/* + * Copyright 2013 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database.exception; + +public class SamsungClipboardException extends Exception { + + public SamsungClipboardException(String message) { + super(message); + } + + public SamsungClipboardException(Exception e) { + super(e); + } + + /** + * + */ + private static final long serialVersionUID = -3168837280393843509L; + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/load/ImporterV3.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/load/ImporterV3.java new file mode 100644 index 00000000..1d233ef5 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/load/ImporterV3.java @@ -0,0 +1,435 @@ +/* + * Copyright 2009-2012 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + * + +Derived from + +KeePass for J2ME + +Copyright 2007 Naomaru Itoi + +This file was derived from + +Java clone of KeePass - A KeePass file viewer for Java +Copyright 2006 Bill Zwicky + +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; version 2 + +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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package com.keepassdroid.database.load; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.security.DigestOutputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import android.util.Log; + +import com.keepassdroid.UpdateStatus; +import com.keepassdroid.crypto.CipherFactory; +import com.keepassdroid.database.PwDatabaseV3; +import com.keepassdroid.database.PwDate; +import com.keepassdroid.database.PwDbHeader; +import com.keepassdroid.database.PwDbHeaderV3; +import com.keepassdroid.database.PwEncryptionAlgorithm; +import com.keepassdroid.database.PwEntryV3; +import com.keepassdroid.database.PwGroupV3; +import com.keepassdroid.database.exception.InvalidAlgorithmException; +import com.keepassdroid.database.exception.InvalidDBException; +import com.keepassdroid.database.exception.InvalidDBSignatureException; +import com.keepassdroid.database.exception.InvalidDBVersionException; +import com.keepassdroid.database.exception.InvalidKeyFileException; +import com.keepassdroid.database.exception.InvalidPasswordException; +import com.keepassdroid.stream.LEDataInputStream; +import com.keepassdroid.stream.LEDataOutputStream; +import com.keepassdroid.stream.NullOutputStream; +import com.keepassdroid.utils.Types; + +/** + * Load a v3 database file. + * + * @author Naomaru Itoi + * @author Bill Zwicky + */ +public class ImporterV3 { + + public ImporterV3() { + super(); + } + + protected PwDatabaseV3 createDB() { + return new PwDatabaseV3(); + } + + /** + * Load a v3 database file, return contents in a new PwDatabaseV3. + * + * @param infile Existing file to load. + * @param password Pass phrase for infile. + * @param pRepair (unused) + * @return new PwDatabaseV3 container. + * + * @throws IOException on any file error. + * @throws InvalidKeyFileException + * @throws InvalidPasswordException + * @throws InvalidPasswordException on a decryption error, or possible internal bug. + * @throws InvalidDBSignatureException + * @throws InvalidDBVersionException + * @throws IllegalBlockSizeException on a decryption error, or possible internal bug. + * @throws BadPaddingException on a decryption error, or possible internal bug. + * @throws NoSuchAlgorithmException on a decryption error, or possible internal bug. + * @throws NoSuchPaddingException on a decryption error, or possible internal bug. + * @throws InvalidAlgorithmParameterException if error decrypting main file body. + * @throws ShortBufferException if error decrypting main file body. + */ + public PwDatabaseV3 openDatabase( InputStream inStream, String password, String keyfile ) + throws IOException, InvalidDBException + { + return openDatabase(inStream, password, keyfile, new UpdateStatus()); + } + + public PwDatabaseV3 openDatabase( InputStream inStream, String password, String keyfile, UpdateStatus status ) + throws IOException, InvalidDBException + { + PwDatabaseV3 newManager; + + + // Load entire file, most of it's encrypted. + int fileSize = inStream.available(); + byte[] filebuf = new byte[fileSize + 16]; // Pad with a blocksize (Twofish uses 128 bits), since Android 4.3 tries to write more to the buffer + inStream.read(filebuf, 0, fileSize); + inStream.close(); + + // Parse header (unencrypted) + if( fileSize < PwDbHeaderV3.BUF_SIZE ) + throw new IOException( "File too short for header: "+fileSize+"<"+PwDbHeaderV3.BUF_SIZE ); + PwDbHeaderV3 hdr = new PwDbHeaderV3(); + hdr.loadFromFile(filebuf, 0 ); + + if( (hdr.signature1 != PwDbHeader.PWM_DBSIG_1) || (hdr.signature2 != PwDbHeaderV3.DBSIG_2) ) { + throw new InvalidDBSignatureException(); + } + + if( !hdr.matchesVersion() ) { + throw new InvalidDBVersionException(); + } + + newManager = createDB(); + newManager.setMasterKey( password, keyfile ); + + // Select algorithm + if( (hdr.flags & PwDbHeaderV3.FLAG_RIJNDAEL) != 0 ) { + newManager.algorithm = PwEncryptionAlgorithm.Rjindal; + } else if( (hdr.flags & PwDbHeaderV3.FLAG_TWOFISH) != 0 ) { + newManager.algorithm = PwEncryptionAlgorithm.Twofish; + } else { + throw new InvalidAlgorithmException(); + } + + // Copy for testing + newManager.copyHeader(hdr); + + newManager.numKeyEncRounds = hdr.numKeyEncRounds; + + newManager.name = "KeePass Password Manager"; + + // Generate transformedMasterKey from masterKey + newManager.makeFinalKey(hdr.masterSeed, hdr.transformSeed, newManager.numKeyEncRounds); + + // Initialize Rijndael algorithm + Cipher cipher; + try { + if ( newManager.algorithm == PwEncryptionAlgorithm.Rjindal ) { + cipher = CipherFactory.getInstance("AES/CBC/PKCS5Padding"); + } else if ( newManager.algorithm == PwEncryptionAlgorithm.Twofish ) { + cipher = CipherFactory.getInstance("TWOFISH/CBC/PKCS7PADDING"); + } else { + throw new IOException( "Encryption algorithm is not supported" ); + } + + } catch (NoSuchAlgorithmException e1) { + throw new IOException("No such algorithm"); + } catch (NoSuchPaddingException e1) { + throw new IOException("No such pdading"); + } + + try { + cipher.init( Cipher.DECRYPT_MODE, new SecretKeySpec( newManager.finalKey, "AES" ), new IvParameterSpec( hdr.encryptionIV ) ); + } catch (InvalidKeyException e1) { + throw new IOException("Invalid key"); + } catch (InvalidAlgorithmParameterException e1) { + throw new IOException("Invalid algorithm parameter."); + } + + // Decrypt! The first bytes aren't encrypted (that's the header) + int encryptedPartSize; + try { + encryptedPartSize = cipher.doFinal(filebuf, PwDbHeaderV3.BUF_SIZE, fileSize - PwDbHeaderV3.BUF_SIZE, filebuf, PwDbHeaderV3.BUF_SIZE ); + } catch (ShortBufferException e1) { + throw new IOException("Buffer too short"); + } catch (IllegalBlockSizeException e1) { + throw new IOException("Invalid block size"); + } catch (BadPaddingException e1) { + throw new InvalidPasswordException(); + } + + // Copy decrypted data for testing + newManager.copyEncrypted(filebuf, PwDbHeaderV3.BUF_SIZE, encryptedPartSize); + + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new IOException("No SHA-256 algorithm"); + } + NullOutputStream nos = new NullOutputStream(); + DigestOutputStream dos = new DigestOutputStream(nos, md); + dos.write(filebuf, PwDbHeaderV3.BUF_SIZE, encryptedPartSize); + dos.close(); + byte[] hash = md.digest(); + + if( ! Arrays.equals(hash, hdr.contentsHash) ) { + + Log.w("KeePassDroid","Database file did not decrypt correctly. (checksum code is broken)"); + throw new InvalidPasswordException(); + } + + // Import all groups + + int pos = PwDbHeaderV3.BUF_SIZE; + PwGroupV3 newGrp = new PwGroupV3(); + for( int i = 0; i < hdr.numGroups; ) { + int fieldType = LEDataInputStream.readUShort( filebuf, pos ); + pos += 2; + int fieldSize = LEDataInputStream.readInt( filebuf, pos ); + pos += 4; + + if( fieldType == 0xFFFF ) { + + // End-Group record. Save group and count it. + newGrp.populateBlankFields(newManager); + newManager.groups.add(newGrp); + newGrp = new PwGroupV3(); + i++; + } + else { + readGroupField(newManager, newGrp, fieldType, filebuf, pos); + } + pos += fieldSize; + } + + // Import all entries + PwEntryV3 newEnt = new PwEntryV3(); + for( int i = 0; i < hdr.numEntries; ) { + int fieldType = LEDataInputStream.readUShort( filebuf, pos ); + int fieldSize = LEDataInputStream.readInt( filebuf, pos + 2 ); + + if( fieldType == 0xFFFF ) { + // End-Group record. Save group and count it. + newEnt.populateBlankFields(newManager); + newManager.entries.add(newEnt); + newEnt = new PwEntryV3(); + i++; + } + else { + readEntryField(newManager, newEnt, filebuf, pos); + } + pos += 2 + 4 + fieldSize; + } + + newManager.constructTree(null); + + return newManager; + } + + /** + * KeePass's custom pad style. + * + * @param data buffer to pad. + * @return addtional bytes to append to data[] to make + * a properly padded array. + */ + public static byte[] makePad( byte[] data ) { + //custom pad method + + // append 0x80 plus zeros to a multiple of 4 bytes + int thisblk = 32 - data.length % 32; // bytes needed to finish blk + int nextblk = 0; // 32 if we need another block + // need 9 bytes; add new block if no room + if( thisblk < 9 ) { + nextblk = 32; + } + + // all bytes are zeroed for free + byte[] pad = new byte[ thisblk + nextblk ]; + pad[0] = (byte)0x80; + + // write length*8 to end of final block + int ix = thisblk + nextblk - 8; + LEDataOutputStream.writeInt( data.length>>29, pad, ix ); + bsw32( pad, ix ); + ix += 4; + LEDataOutputStream.writeInt( data.length<<3, pad, ix ); + bsw32( pad, ix ); + + return pad; + } + + public static void bsw32( byte[] ary, int offset ) { + byte t = ary[offset]; + ary[offset] = ary[offset+3]; + ary[offset+3] = t; + t = ary[offset+1]; + ary[offset+1] = ary[offset+2]; + ary[offset+2] = t; + } + + + /** + * Parse and save one record from binary file. + * @param buf + * @param offset + * @return If >0, + * @throws UnsupportedEncodingException + */ + void readGroupField(PwDatabaseV3 db, PwGroupV3 grp, int fieldType, byte[] buf, int offset) throws UnsupportedEncodingException { + switch( fieldType ) { + case 0x0000 : + // Ignore field + break; + case 0x0001 : + grp.groupId = LEDataInputStream.readInt(buf, offset); + break; + case 0x0002 : + grp.name = Types.readCString(buf, offset); + break; + case 0x0003 : + grp.tCreation = new PwDate(buf, offset); + break; + case 0x0004 : + grp.tLastMod = new PwDate(buf, offset); + break; + case 0x0005 : + grp.tLastAccess = new PwDate(buf, offset); + break; + case 0x0006 : + grp.tExpire = new PwDate(buf, offset); + break; + case 0x0007 : + grp.icon = db.iconFactory.getIcon(LEDataInputStream.readInt(buf, offset)); + break; + case 0x0008 : + grp.level = LEDataInputStream.readUShort(buf, offset); + break; + case 0x0009 : + grp.flags = LEDataInputStream.readInt(buf, offset); + break; + } + } + + + + void readEntryField(PwDatabaseV3 db, PwEntryV3 ent, byte[] buf, int offset) + throws UnsupportedEncodingException + { + int fieldType = LEDataInputStream.readUShort(buf, offset); + offset += 2; + int fieldSize = LEDataInputStream.readInt(buf, offset); + offset += 4; + + switch( fieldType ) { + case 0x0000 : + // Ignore field + break; + case 0x0001 : + ent.setUUID(Types.bytestoUUID(buf, offset)); + break; + case 0x0002 : + ent.groupId = LEDataInputStream.readInt(buf, offset); + break; + case 0x0003 : + int iconId = LEDataInputStream.readInt(buf, offset); + + // Clean up after bug that set icon ids to -1 + if (iconId == -1) { + iconId = 0; + } + + ent.icon = db.iconFactory.getIcon(iconId); + break; + case 0x0004 : + ent.title = Types.readCString(buf, offset); + break; + case 0x0005 : + ent.url = Types.readCString(buf, offset); + break; + case 0x0006 : + ent.username = Types.readCString(buf, offset); + break; + case 0x0007 : + ent.setPassword(buf, offset, Types.strlen(buf, offset)); + break; + case 0x0008 : + ent.additional = Types.readCString(buf, offset); + break; + case 0x0009 : + ent.tCreation = new PwDate(buf, offset); + break; + case 0x000A : + ent.tLastMod = new PwDate(buf, offset); + break; + case 0x000B : + ent.tLastAccess = new PwDate(buf, offset); + break; + case 0x000C : + ent.tExpire = new PwDate(buf, offset); + break; + case 0x000D : + ent.binaryDesc = Types.readCString(buf, offset); + break; + case 0x000E : + ent.setBinaryData(buf, offset, fieldSize); + break; + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/save/PwDbHeaderOutput.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/save/PwDbHeaderOutput.java new file mode 100644 index 00000000..58423fda --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/save/PwDbHeaderOutput.java @@ -0,0 +1,27 @@ +/* + * Copyright 2013 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database.save; + +public class PwDbHeaderOutput { + protected byte[] hashOfHeader = null; + + public byte[] getHashOfHeader() { return hashOfHeader; } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/save/PwDbHeaderOutputV3.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/save/PwDbHeaderOutputV3.java new file mode 100644 index 00000000..d80ffaad --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/save/PwDbHeaderOutputV3.java @@ -0,0 +1,56 @@ +/* + * Copyright 2009 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database.save; + +import java.io.IOException; +import java.io.OutputStream; + + +import com.keepassdroid.database.PwDbHeaderV3; +import com.keepassdroid.stream.LEDataOutputStream; + +public class PwDbHeaderOutputV3 { + private PwDbHeaderV3 mHeader; + private OutputStream mOS; + + public PwDbHeaderOutputV3(PwDbHeaderV3 header, OutputStream os) { + mHeader = header; + mOS = os; + } + + public void output() throws IOException { + mOS.write(LEDataOutputStream.writeIntBuf(mHeader.signature1)); + mOS.write(LEDataOutputStream.writeIntBuf(mHeader.signature2)); + mOS.write(LEDataOutputStream.writeIntBuf(mHeader.flags)); + mOS.write(LEDataOutputStream.writeIntBuf(mHeader.version)); + mOS.write(mHeader.masterSeed); + mOS.write(mHeader.encryptionIV); + mOS.write(LEDataOutputStream.writeIntBuf(mHeader.numGroups)); + mOS.write(LEDataOutputStream.writeIntBuf(mHeader.numEntries)); + mOS.write(mHeader.contentsHash); + mOS.write(mHeader.transformSeed); + mOS.write(LEDataOutputStream.writeIntBuf(mHeader.numKeyEncRounds)); + + } + + public void close() throws IOException { + mOS.close(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/save/PwDbOutput.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/save/PwDbOutput.java new file mode 100644 index 00000000..38e938ef --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/save/PwDbOutput.java @@ -0,0 +1,64 @@ +/* + * Copyright 2010-2013 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database.save; + +import java.io.OutputStream; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +import com.keepassdroid.database.PwDatabaseV3; +import com.keepassdroid.database.PwDbHeader; +import com.keepassdroid.database.exception.PwDbOutputException; + +public abstract class PwDbOutput { + + protected OutputStream mOS; + + public static PwDbOutput getInstance(PwDatabaseV3 pm, OutputStream os) { + if ( pm instanceof PwDatabaseV3 ) { + return new PwDbV3Output((PwDatabaseV3)pm, os); + } + + return null; + } + + protected PwDbOutput(OutputStream os) { + mOS = os; + } + + protected SecureRandom setIVs(PwDbHeader header) throws PwDbOutputException { + SecureRandom random; + try { + random = SecureRandom.getInstance("SHA1PRNG"); + } catch (NoSuchAlgorithmException e) { + throw new PwDbOutputException("Does not support secure random number generation."); + } + random.nextBytes(header.encryptionIV); + random.nextBytes(header.masterSeed); + random.nextBytes(header.transformSeed); + + return random; + } + + public abstract void output() throws PwDbOutputException; + + public abstract PwDbHeader outputHeader(OutputStream os) throws PwDbOutputException; + +} \ No newline at end of file diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/save/PwDbV3Output.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/save/PwDbV3Output.java new file mode 100644 index 00000000..98da9fac --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/save/PwDbV3Output.java @@ -0,0 +1,212 @@ +/* +` * Copyright 2009-2011 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database.save; + +import java.io.BufferedOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.security.DigestOutputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; + +import javax.crypto.Cipher; +import javax.crypto.CipherOutputStream; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import com.keepassdroid.crypto.CipherFactory; +import com.keepassdroid.database.PwDatabaseV3; +import com.keepassdroid.database.PwDbHeader; +import com.keepassdroid.database.PwDbHeaderV3; +import com.keepassdroid.database.PwEncryptionAlgorithm; +import com.keepassdroid.database.PwEntryV3; +import com.keepassdroid.database.PwGroupV3; +import com.keepassdroid.database.exception.PwDbOutputException; +import com.keepassdroid.stream.NullOutputStream; + +public class PwDbV3Output extends PwDbOutput { + private PwDatabaseV3 mPM; + + public PwDbV3Output(PwDatabaseV3 pm, OutputStream os) { + super(os); + + mPM = pm; + } + + public byte[] getFinalKey(PwDbHeader header) throws PwDbOutputException { + try { + mPM.makeFinalKey(header.masterSeed, header.transformSeed, mPM.numKeyEncRounds); + return mPM.finalKey; + } catch (IOException e) { + throw new PwDbOutputException("Key creation failed: " + e.getMessage()); + } + } + + @Override + public void output() throws PwDbOutputException { + prepForOutput(); + + PwDbHeader header = outputHeader(mOS); + + byte[] finalKey = getFinalKey(header); + + Cipher cipher; + try { + if (mPM.algorithm == PwEncryptionAlgorithm.Rjindal) { + cipher = CipherFactory.getInstance("AES/CBC/PKCS5Padding"); + } else if (mPM.algorithm == PwEncryptionAlgorithm.Twofish){ + cipher = CipherFactory.getInstance("TWOFISH/CBC/PKCS7PADDING"); + } else { + throw new Exception(); + } + } catch (Exception e) { + throw new PwDbOutputException("Algorithm not supported."); + } + + try { + cipher.init( Cipher.ENCRYPT_MODE, new SecretKeySpec(finalKey, "AES" ), new IvParameterSpec(header.encryptionIV) ); + CipherOutputStream cos = new CipherOutputStream(mOS, cipher); + BufferedOutputStream bos = new BufferedOutputStream(cos); + outputPlanGroupAndEntries(bos); + bos.flush(); + bos.close(); + + } catch (InvalidKeyException e) { + throw new PwDbOutputException("Invalid key"); + } catch (InvalidAlgorithmParameterException e) { + throw new PwDbOutputException("Invalid algorithm parameter."); + } catch (IOException e) { + throw new PwDbOutputException("Failed to output final encrypted part."); + } + } + + private void prepForOutput() { + // Before we output the header, we should sort our list of groups and remove any orphaned nodes that are no longer part of the group hierarchy + sortGroupsForOutput(); + } + + public PwDbHeader outputHeader(OutputStream os) throws PwDbOutputException { + // Build header + PwDbHeaderV3 header = new PwDbHeaderV3(); + header.signature1 = PwDbHeader.PWM_DBSIG_1; + header.signature2 = PwDbHeaderV3.DBSIG_2; + header.flags = PwDbHeaderV3.FLAG_SHA2; + + if ( mPM.getEncAlgorithm() == PwEncryptionAlgorithm.Rjindal ) { + header.flags |= PwDbHeaderV3.FLAG_RIJNDAEL; + } else if ( mPM.getEncAlgorithm() == PwEncryptionAlgorithm.Twofish ) { + header.flags |= PwDbHeaderV3.FLAG_TWOFISH; + } else { + throw new PwDbOutputException("Unsupported algorithm."); + } + + header.version = PwDbHeaderV3.DBVER_DW; + header.numGroups = mPM.getGroups().size(); + header.numEntries = mPM.entries.size(); + header.numKeyEncRounds = mPM.getNumKeyEncRecords(); + + setIVs(header); + + // Write checksum Checksum + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new PwDbOutputException("SHA-256 not implemented here."); + } + + NullOutputStream nos; + nos = new NullOutputStream(); + DigestOutputStream dos = new DigestOutputStream(nos, md); + BufferedOutputStream bos = new BufferedOutputStream(dos); + try { + outputPlanGroupAndEntries(bos); + bos.flush(); + bos.close(); + } catch (IOException e) { + throw new PwDbOutputException("Failed to generate checksum."); + } + + header.contentsHash = md.digest(); + + // Output header + PwDbHeaderOutputV3 pho = new PwDbHeaderOutputV3(header, os); + try { + pho.output(); + } catch (IOException e) { + throw new PwDbOutputException("Failed to output the header."); + } + + return header; + } + + public void outputPlanGroupAndEntries(OutputStream os) throws PwDbOutputException { + //long size = 0; + + // Groups + List groups = mPM.getGroups(); + for ( int i = 0; i < groups.size(); i++ ) { + PwGroupV3 pg = (PwGroupV3) groups.get(i); + PwGroupOutputV3 pgo = new PwGroupOutputV3(pg, os); + try { + pgo.output(); + } catch (IOException e) { + throw new PwDbOutputException("Failed to output a group: " + e.getMessage()); + } + } + + // Entries + for (int i = 0; i < mPM.entries.size(); i++ ) { + PwEntryV3 pe = (PwEntryV3) mPM.entries.get(i); + PwEntryOutputV3 peo = new PwEntryOutputV3(pe, os); + try { + peo.output(); + } catch (IOException e) { + throw new PwDbOutputException("Failed to output an entry."); + } + } + } + + private void sortGroupsForOutput() { + List groupList = new ArrayList(); + + // Rebuild list according to coalation sorting order removing any orphaned groups + List roots = mPM.getGrpRoots(); + for ( int i = 0; i < roots.size(); i++ ) { + sortGroup((PwGroupV3) roots.get(i), groupList); + } + + mPM.setGroups(groupList); + } + + private void sortGroup(PwGroupV3 group, List groupList) { + // Add current group + groupList.add(group); + + // Recurse over children + for ( int i = 0; i < group.childGroups.size(); i++ ) { + sortGroup((PwGroupV3) group.childGroups.get(i), groupList); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/save/PwEntryOutputV3.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/save/PwEntryOutputV3.java new file mode 100644 index 00000000..2ecc2e08 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/save/PwEntryOutputV3.java @@ -0,0 +1,177 @@ +/* + * Copyright 2009 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database.save; + +import java.io.IOException; +import java.io.OutputStream; + +import com.keepassdroid.database.PwEntryV3; +import com.keepassdroid.stream.LEDataOutputStream; +import com.keepassdroid.utils.Types; + +public class PwEntryOutputV3 { + // Constants + public static final byte[] UUID_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(1); + public static final byte[] GROUPID_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(2); + public static final byte[] IMAGEID_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(3); + public static final byte[] TITLE_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(4); + public static final byte[] URL_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(5); + public static final byte[] USERNAME_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(6); + public static final byte[] PASSWORD_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(7); + public static final byte[] ADDITIONAL_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(8); + public static final byte[] CREATE_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(9); + public static final byte[] MOD_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(10); + public static final byte[] ACCESS_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(11); + public static final byte[] EXPIRE_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(12); + public static final byte[] BINARY_DESC_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(13); + public static final byte[] BINARY_DATA_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(14); + public static final byte[] END_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(0xFFFF); + public static final byte[] LONG_FOUR = LEDataOutputStream.writeIntBuf(4); + public static final byte[] UUID_FIELD_SIZE = LEDataOutputStream.writeIntBuf(16); + public static final byte[] DATE_FIELD_SIZE = LEDataOutputStream.writeIntBuf(5); + public static final byte[] IMAGEID_FIELD_SIZE = LONG_FOUR; + public static final byte[] LEVEL_FIELD_SIZE = LONG_FOUR; + public static final byte[] FLAGS_FIELD_SIZE = LONG_FOUR; + public static final byte[] ZERO_FIELD_SIZE = LEDataOutputStream.writeIntBuf(0); + public static final byte[] ZERO_FIVE = {0x00, 0x00, 0x00, 0x00, 0x00}; + public static final byte[] TEST = {0x33, 0x33, 0x33, 0x33}; + + private OutputStream mOS; + private PwEntryV3 mPE; + private long outputBytes = 0; + + /** Output the PwGroupV3 to the stream + * @param pe + * @param os + */ + public PwEntryOutputV3(PwEntryV3 pe, OutputStream os) { + mPE = pe; + mOS = os; + } + + //NOTE: Need be to careful about using ints. The actual type written to file is a unsigned int + public void output() throws IOException { + + outputBytes += 134; // Length of fixed size fields + + // UUID + mOS.write(UUID_FIELD_TYPE); + mOS.write(UUID_FIELD_SIZE); + mOS.write(Types.UUIDtoBytes(mPE.getUUID())); + + // Group ID + mOS.write(GROUPID_FIELD_TYPE); + mOS.write(LONG_FOUR); + mOS.write(LEDataOutputStream.writeIntBuf(mPE.groupId)); + + // Image ID + mOS.write(IMAGEID_FIELD_TYPE); + mOS.write(LONG_FOUR); + mOS.write(LEDataOutputStream.writeIntBuf(mPE.icon.iconId)); + + // Title + //byte[] title = mPE.title.getBytes("UTF-8"); + mOS.write(TITLE_FIELD_TYPE); + int titleLen = Types.writeCString(mPE.title, mOS); + outputBytes += titleLen; + + // URL + mOS.write(URL_FIELD_TYPE); + int urlLen = Types.writeCString(mPE.url, mOS); + outputBytes += urlLen; + + // Username + mOS.write(USERNAME_FIELD_TYPE); + int userLen = Types.writeCString(mPE.username, mOS); + outputBytes += userLen; + + // Password + byte[] password = mPE.getPasswordBytes(); + mOS.write(PASSWORD_FIELD_TYPE); + mOS.write(LEDataOutputStream.writeIntBuf(password.length+1)); + mOS.write(password); + mOS.write(0); + outputBytes += password.length + 1; + + // Additional + mOS.write(ADDITIONAL_FIELD_TYPE); + int addlLen = Types.writeCString(mPE.additional, mOS); + outputBytes += addlLen; + + // Create date + writeDate(CREATE_FIELD_TYPE, mPE.tCreation.getCDate()); + + // Modification date + writeDate(MOD_FIELD_TYPE, mPE.tLastMod.getCDate()); + + // Access date + writeDate(ACCESS_FIELD_TYPE, mPE.tLastAccess.getCDate()); + + // Expiration date + writeDate(EXPIRE_FIELD_TYPE, mPE.tExpire.getCDate()); + + // Binary desc + mOS.write(BINARY_DESC_FIELD_TYPE); + int descLen = Types.writeCString(mPE.binaryDesc, mOS); + outputBytes += descLen; + + // Binary data + int dataLen = writeByteArray(mPE.getBinaryData()); + outputBytes += dataLen; + + // End + mOS.write(END_FIELD_TYPE); + mOS.write(ZERO_FIELD_SIZE); + } + + private int writeByteArray(byte[] data) throws IOException { + int dataLen; + if ( data != null ) { + dataLen = data.length; + } else { + dataLen = 0; + } + mOS.write(BINARY_DATA_FIELD_TYPE); + mOS.write(LEDataOutputStream.writeIntBuf(dataLen)); + if ( data != null ) { + mOS.write(data); + } + + return dataLen; + + } + + private void writeDate(byte[] type, byte[] date) throws IOException { + mOS.write(type); + mOS.write(DATE_FIELD_SIZE); + if ( date != null ) { + mOS.write(date); + } else { + mOS.write(ZERO_FIVE); + } + } + + /** Returns the number of bytes written by the stream + * @return Number of bytes written + */ + public long getLength() { + return outputBytes; + } +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/save/PwGroupOutputV3.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/save/PwGroupOutputV3.java new file mode 100644 index 00000000..9b448936 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/save/PwGroupOutputV3.java @@ -0,0 +1,114 @@ +/* + * Copyright 2009 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database.save; + +import java.io.IOException; +import java.io.OutputStream; + + +import com.keepassdroid.database.PwGroupV3; +import com.keepassdroid.stream.LEDataOutputStream; +import com.keepassdroid.utils.Types; + +public class PwGroupOutputV3 { + // Constants + public static final byte[] GROUPID_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(1); + public static final byte[] NAME_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(2); + public static final byte[] CREATE_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(3); + public static final byte[] MOD_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(4); + public static final byte[] ACCESS_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(5); + public static final byte[] EXPIRE_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(6); + public static final byte[] IMAGEID_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(7); + public static final byte[] LEVEL_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(8); + public static final byte[] FLAGS_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(9); + public static final byte[] END_FIELD_TYPE = LEDataOutputStream.writeUShortBuf(0xFFFF); + public static final byte[] LONG_FOUR = LEDataOutputStream.writeIntBuf(4); + public static final byte[] GROUPID_FIELD_SIZE = LONG_FOUR; + public static final byte[] DATE_FIELD_SIZE = LEDataOutputStream.writeIntBuf(5); + public static final byte[] IMAGEID_FIELD_SIZE = LONG_FOUR; + public static final byte[] LEVEL_FIELD_SIZE = LEDataOutputStream.writeIntBuf(2); + public static final byte[] FLAGS_FIELD_SIZE = LONG_FOUR; + public static final byte[] ZERO_FIELD_SIZE = LEDataOutputStream.writeIntBuf(0); + + private OutputStream mOS; + private PwGroupV3 mPG; + + /** Output the PwGroupV3 to the stream + * @param pg + * @param os + */ + public PwGroupOutputV3(PwGroupV3 pg, OutputStream os) { + mPG = pg; + mOS = os; + } + + public void output() throws IOException { + //NOTE: Need be to careful about using ints. The actual type written to file is a unsigned int, but most values can't be greater than 2^31, so it probably doesn't matter. + + // Group ID + mOS.write(GROUPID_FIELD_TYPE); + mOS.write(GROUPID_FIELD_SIZE); + mOS.write(LEDataOutputStream.writeIntBuf(mPG.groupId)); + + // Name + mOS.write(NAME_FIELD_TYPE); + Types.writeCString(mPG.name, mOS); + + // Create date + mOS.write(CREATE_FIELD_TYPE); + mOS.write(DATE_FIELD_SIZE); + mOS.write(mPG.tCreation.getCDate()); + + // Modification date + mOS.write(MOD_FIELD_TYPE); + mOS.write(DATE_FIELD_SIZE); + mOS.write(mPG.tLastMod.getCDate()); + + // Access date + mOS.write(ACCESS_FIELD_TYPE); + mOS.write(DATE_FIELD_SIZE); + mOS.write(mPG.tLastAccess.getCDate()); + + // Expiration date + mOS.write(EXPIRE_FIELD_TYPE); + mOS.write(DATE_FIELD_SIZE); + mOS.write(mPG.tExpire.getCDate()); + + // Image ID + mOS.write(IMAGEID_FIELD_TYPE); + mOS.write(IMAGEID_FIELD_SIZE); + mOS.write(LEDataOutputStream.writeIntBuf(mPG.icon.iconId)); + + // Level + mOS.write(LEVEL_FIELD_TYPE); + mOS.write(LEVEL_FIELD_SIZE); + mOS.write(LEDataOutputStream.writeUShortBuf(mPG.level)); + + // Flags + mOS.write(FLAGS_FIELD_TYPE); + mOS.write(FLAGS_FIELD_SIZE); + mOS.write(LEDataOutputStream.writeIntBuf(mPG.flags)); + + // End + mOS.write(END_FIELD_TYPE); + mOS.write(ZERO_FIELD_SIZE); + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/security/ProtectedBinary.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/security/ProtectedBinary.java new file mode 100644 index 00000000..e62f94a5 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/security/ProtectedBinary.java @@ -0,0 +1,65 @@ +/* + * Copyright 2013 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database.security; + +import java.util.Arrays; + +public class ProtectedBinary { + + public final static ProtectedBinary EMPTY = new ProtectedBinary(); + + private byte[] data; + private boolean protect; + + public boolean isProtected() { + return protect; + } + + public int length() { + if (data == null) { + return 0; + } + + return data.length; + } + + public ProtectedBinary() { + this(false, new byte[0]); + + } + + public ProtectedBinary(boolean enableProtection, byte[] data) { + protect = enableProtection; + this.data = data; + + } + + + // TODO: replace the byte[] with something like ByteBuffer to make the return + // value immutable, so we don't have to worry about making deep copies + public byte[] getData() { + return data; + } + + public boolean equals(ProtectedBinary rhs) { + return (protect == rhs.protect) && Arrays.equals(data, rhs.data); + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/security/ProtectedString.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/security/ProtectedString.java new file mode 100644 index 00000000..c9052823 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/database/security/ProtectedString.java @@ -0,0 +1,54 @@ +/* + * Copyright 2013 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.database.security; + +public class ProtectedString { + + private String string; + private boolean protect; + + public boolean isProtected() { + return protect; + } + + public int length() { + if (string == null) { + return 0; + } + + return string.length(); + } + + public ProtectedString() { + this(false, ""); + + } + + public ProtectedString(boolean enableProtection, String string) { + protect = enableProtection; + this.string = string; + + } + + public String toString() { + return string; + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/BetterCipherInputStream.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/BetterCipherInputStream.java new file mode 100644 index 00000000..b9196400 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/BetterCipherInputStream.java @@ -0,0 +1,247 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.keepassdroid.stream; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.GeneralSecurityException; + +import javax.crypto.Cipher; +import javax.crypto.NullCipher; + +/** + * This class wraps an {@code InputStream} and a cipher so that {@code read()} + * methods return data that are read from the underlying {@code InputStream} and + * processed by the cipher. + *

+ * The cipher must be initialized for the requested operation before being used + * by a {@code BetterCipherInputStream}. For example, if a cipher initialized for + * decryption is used with a {@code BetterCipherInputStream}, the {@code + * BetterCipherInputStream} tries to read the data an decrypt them before returning. + */ +public class BetterCipherInputStream extends FilterInputStream { + + private final Cipher cipher; + private static final int I_DEFAULT_BUFFER_SIZE = 8 * 1024; + private final byte[] i_buffer; + private int index; // index of the bytes to return from o_buffer + private byte[] o_buffer; + private boolean finished; + + /** + * Creates a new {@code BetterCipherInputStream} instance for an {@code + * InputStream} and a cipher. + * + * @param is + * the input stream to read data from. + * @param c + * the cipher to process the data with. + */ + public BetterCipherInputStream(InputStream is, Cipher c) { + this(is, c, I_DEFAULT_BUFFER_SIZE); + } + + /** + * Creates a new {@code BetterCipherInputStream} instance for an {@code + * InputStream} and a cipher. + * + * @param is + * the input stream to read data from. + * @param c + * the cipher to process the data with. + * @param bufferSize + * size to buffer output from the cipher + */ + public BetterCipherInputStream(InputStream is, Cipher c, int bufferSize) { + super(is); + this.cipher = c; + i_buffer = new byte[bufferSize]; + } + + /** + * Creates a new {@code BetterCipherInputStream} instance for an {@code + * InputStream} without a cipher. + *

+ * A {@code NullCipher} is created and used to process the data. + * + * @param is + * the input stream to read data from. + */ + protected BetterCipherInputStream(InputStream is) { + this(is, new NullCipher()); + } + + /** + * Reads the next byte from this cipher input stream. + * + * @return the next byte, or {@code -1} if the end of the stream is reached. + * @throws IOException + * if an error occurs. + */ + @Override + public int read() throws IOException { + if (finished) { + return ((o_buffer == null) || (index == o_buffer.length)) + ? -1 + : o_buffer[index++] & 0xFF; + } + if ((o_buffer != null) && (index < o_buffer.length)) { + return o_buffer[index++] & 0xFF; + } + index = 0; + o_buffer = null; + int num_read; + while (o_buffer == null) { + if ((num_read = in.read(i_buffer)) == -1) { + try { + o_buffer = cipher.doFinal(); + } catch (Exception e) { + throw new IOException(e.getMessage()); + } + finished = true; + break; + } + o_buffer = cipher.update(i_buffer, 0, num_read); + } + return read(); + } + + /** + * Reads the next {@code b.length} bytes from this input stream into buffer + * {@code b}. + * + * @param b + * the buffer to be filled with data. + * @return the number of bytes filled into buffer {@code b}, or {@code -1} + * if the end of the stream is reached. + * @throws IOException + * if an error occurs. + */ + @Override + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + + /** + * Reads the next {@code len} bytes from this input stream into buffer + * {@code b} starting at offset {@code off}. + *

+ * if {@code b} is {@code null}, the next {@code len} bytes are read and + * discarded. + * + * @param b + * the buffer to be filled with data. + * @param off + * the offset to start in the buffer. + * @param len + * the maximum number of bytes to read. + * @return the number of bytes filled into buffer {@code b}, or {@code -1} + * of the of the stream is reached. + * @throws IOException + * if an error occurs. + * @throws NullPointerException + * if the underlying input stream is {@code null}. + */ + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (in == null) { + throw new NullPointerException("Underlying input stream is null"); + } + + int read_b; + int i; + for (i=0; i + * The number of bytes skipped depends on the result of a call to + * {@link BetterCipherInputStream#available() available}. The smaller of n and the + * result are the number of bytes being skipped. + * + * @param n + * the number of bytes that should be skipped. + * @return the number of bytes actually skipped. + * @throws IOException + * if an error occurs + */ + @Override + public long skip(long n) throws IOException { + long i = 0; + int available = available(); + if (available < n) { + n = available; + } + while ((i < n) && (read() != -1)) { + i++; + } + return i; + } + + /** + * Returns the number of bytes available without blocking. + * + * @return the number of bytes available, currently zero. + * @throws IOException + * if an error occurs + */ + @Override + public int available() throws IOException { + return 0; + } + + /** + * Closes this {@code BetterCipherInputStream}, also closes the underlying input + * stream and call {@code doFinal} on the cipher object. + * + * @throws IOException + * if an error occurs. + */ + @Override + public void close() throws IOException { + in.close(); + try { + cipher.doFinal(); + } catch (GeneralSecurityException ignore) { + //do like RI does + } + + } + + /** + * Returns whether this input stream supports {@code mark} and + * {@code reset}, which it does not. + * + * @return false, since this input stream does not support {@code mark} and + * {@code reset}. + */ + @Override + public boolean markSupported() { + return false; + } +} \ No newline at end of file diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/CopyInputStream.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/CopyInputStream.java new file mode 100644 index 00000000..7c4a0289 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/CopyInputStream.java @@ -0,0 +1,103 @@ +/* + * Copyright 2011 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.stream; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * This class copies everything pulled through its input stream into the + * output stream. + */ +public class CopyInputStream extends InputStream { + private InputStream is; + private OutputStream os; + + public CopyInputStream(InputStream is, OutputStream os) { + this.is = is; + this.os = os; + } + + @Override + public int available() throws IOException { + return is.available(); + } + + @Override + public void close() throws IOException { + is.close(); + os.close(); + } + + @Override + public void mark(int readlimit) { + is.mark(readlimit); + } + + @Override + public boolean markSupported() { + return is.markSupported(); + } + + @Override + public int read() throws IOException { + int data = is.read(); + + if (data != -1) { + os.write(data); + } + + return data; + } + + @Override + public int read(byte[] b, int offset, int length) throws IOException { + int len = is.read(b, offset, length); + + if (len != -1) { + os.write(b, offset, len); + } + + return len; + } + + @Override + public int read(byte[] b) throws IOException { + int len = is.read(b); + + if (len != -1) { + os.write(b, 0, len); + } + + return len; + } + + @Override + public synchronized void reset() throws IOException { + is.reset(); + } + + @Override + public long skip(long byteCount) throws IOException { + return is.skip(byteCount); + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/CountInputStream.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/CountInputStream.java new file mode 100644 index 00000000..0fbfc728 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/CountInputStream.java @@ -0,0 +1,82 @@ +/* + * Copyright 2013 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.stream; + +import java.io.IOException; +import java.io.InputStream; + +public class CountInputStream extends InputStream { + InputStream is; + long bytes = 0; + + public CountInputStream(InputStream is) { + this.is = is; + } + + @Override + public int available() throws IOException { + return is.available(); + } + + @Override + public void close() throws IOException { + is.close(); + } + + @Override + public void mark(int readlimit) { + is.mark(readlimit); + } + + @Override + public boolean markSupported() { + return is.markSupported(); + } + + @Override + public int read() throws IOException { + bytes++; + return is.read(); + } + + @Override + public int read(byte[] buffer, int offset, int length) throws IOException { + bytes += length; + return is.read(buffer, offset, length); + } + + @Override + public int read(byte[] buffer) throws IOException { + bytes += buffer.length; + return is.read(buffer); + } + + @Override + public synchronized void reset() throws IOException { + is.reset(); + } + + @Override + public long skip(long byteCount) throws IOException { + bytes += byteCount; + return is.skip(byteCount); + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/CountOutputStream.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/CountOutputStream.java new file mode 100644 index 00000000..0c7f2ada --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/CountOutputStream.java @@ -0,0 +1,62 @@ +/* + * Copyright 2013 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.stream; + +import java.io.IOException; +import java.io.OutputStream; + +public class CountOutputStream extends OutputStream { + OutputStream os; + long bytes = 0; + + public CountOutputStream(OutputStream os) { + this.os = os; + } + + + @Override + public void close() throws IOException { + os.close(); + } + + @Override + public void flush() throws IOException { + os.flush(); + } + + + @Override + public void write(byte[] buffer, int offset, int count) throws IOException { + bytes += count; + os.write(buffer, offset, count); + } + + @Override + public void write(byte[] buffer) throws IOException { + bytes += buffer.length; + os.write(buffer); + } + + @Override + public void write(int oneByte) throws IOException { + bytes++; + os.write(oneByte); + } +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/HashedBlockInputStream.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/HashedBlockInputStream.java new file mode 100644 index 00000000..fdc2b820 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/HashedBlockInputStream.java @@ -0,0 +1,163 @@ +/* + * Copyright 2010 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.stream; + +import java.io.IOException; +import java.io.InputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + + +public class HashedBlockInputStream extends InputStream { + + private final static int HASH_SIZE = 32; + + private LEDataInputStream baseStream; + private int bufferPos = 0; + private byte[] buffer = new byte[0]; + private long bufferIndex = 0; + private boolean atEnd = false; + + + @Override + public int read(byte[] b) throws IOException { + return read(b, 0, b.length); + } + + public HashedBlockInputStream(InputStream is) { + baseStream = new LEDataInputStream(is); + } + + @Override + public int read(byte[] b, int offset, int length) throws IOException { + if ( atEnd ) return -1; + + int remaining = length; + + while ( remaining > 0 ) { + if ( bufferPos == buffer.length ) { + // Get more from the source into the buffer + if ( ! ReadHashedBlock() ) { + return length - remaining; + } + + } + + // Copy from buffer out + int copyLen = Math.min(buffer.length - bufferPos, remaining); + + System.arraycopy(buffer, bufferPos, b, offset, copyLen); + + offset += copyLen; + bufferPos += copyLen; + + remaining -= copyLen; + } + + return length; + } + + /** + * @return false, when the end of the source stream is reached + * @throws IOException + */ + private boolean ReadHashedBlock() throws IOException { + if ( atEnd ) return false; + + bufferPos = 0; + + long index = baseStream.readUInt(); + if ( index != bufferIndex ) { + throw new IOException("Invalid data format"); + } + bufferIndex++; + + byte[] storedHash = baseStream.readBytes(32); + if ( storedHash == null || storedHash.length != HASH_SIZE) { + throw new IOException("Invalid data format"); + } + + int bufferSize = LEDataInputStream.readInt(baseStream); + if ( bufferSize < 0 ) { + throw new IOException("Invalid data format"); + } + + if ( bufferSize == 0 ) { + for (int hash = 0; hash < HASH_SIZE; hash++) { + if ( storedHash[hash] != 0 ) { + throw new IOException("Invalid data format"); + } + } + + atEnd = true; + buffer = new byte[0]; + return false; + } + + buffer = baseStream.readBytes(bufferSize); + if ( buffer == null || buffer.length != bufferSize ) { + throw new IOException("Invalid data format"); + } + + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new IOException("SHA-256 not implemented here."); + } + + byte[] computedHash = md.digest(buffer); + if ( computedHash == null || computedHash.length != HASH_SIZE ) { + throw new IOException("Hash wrong size"); + } + + if ( ! Arrays.equals(storedHash, computedHash) ) { + throw new IOException("Hashes didn't match."); + } + + return true; + } + + @Override + public long skip(long n) throws IOException { + return 0; + } + + @Override + public int read() throws IOException { + if ( atEnd ) return -1; + + if ( bufferPos == buffer.length ) { + if ( ! ReadHashedBlock() ) return -1; + } + + int output = buffer[bufferPos]; + bufferPos++; + + return output; + } + + @Override + public void close() throws IOException { + baseStream.close(); + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/HashedBlockOutputStream.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/HashedBlockOutputStream.java new file mode 100644 index 00000000..f78b2b57 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/HashedBlockOutputStream.java @@ -0,0 +1,148 @@ +/* + * Copyright 2010 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.stream; + +import java.io.IOException; +import java.io.OutputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class HashedBlockOutputStream extends OutputStream { + + private final static int DEFAULT_BUFFER_SIZE = 1024 * 1024; + + private LEDataOutputStream baseStream; + private int bufferPos = 0; + private byte[] buffer; + private long bufferIndex = 0; + + public HashedBlockOutputStream(OutputStream os) { + init(os, DEFAULT_BUFFER_SIZE); + } + + public HashedBlockOutputStream(OutputStream os, int bufferSize) { + if ( bufferSize <= 0 ) { + bufferSize = DEFAULT_BUFFER_SIZE; + } + + init(os, bufferSize); + } + + private void init(OutputStream os, int bufferSize) { + baseStream = new LEDataOutputStream(os); + buffer = new byte[bufferSize]; + + } + + @Override + public void write(int oneByte) throws IOException { + byte[] buf = new byte[1]; + buf[0] = (byte)oneByte; + write(buf, 0, 1); + } + + @Override + public void close() throws IOException { + if ( bufferPos != 0 ) { + // Write remaining buffered amount + WriteHashedBlock(); + } + + // Write terminating block + WriteHashedBlock(); + + flush(); + baseStream.close(); + } + + @Override + public void flush() throws IOException { + baseStream.flush(); + } + + @Override + public void write(byte[] b, int offset, int count) throws IOException { + while ( count > 0 ) { + if ( bufferPos == buffer.length ) { + WriteHashedBlock(); + } + + int copyLen = Math.min(buffer.length - bufferPos, count); + + System.arraycopy(b, offset, buffer, bufferPos, copyLen); + + offset += copyLen; + bufferPos += copyLen; + + count -= copyLen; + } + } + + private void WriteHashedBlock() throws IOException { + baseStream.writeUInt(bufferIndex); + bufferIndex++; + + if ( bufferPos > 0 ) { + MessageDigest md = null; + try { + md = MessageDigest.getInstance("SHA-256"); + } catch (NoSuchAlgorithmException e) { + throw new IOException("SHA-256 not implemented here."); + } + + byte[] hash; + md.update(buffer, 0, bufferPos); + hash = md.digest(); + /* + if ( bufferPos == buffer.length) { + hash = md.digest(buffer); + } else { + byte[] b = new byte[bufferPos]; + System.arraycopy(buffer, 0, b, 0, bufferPos); + hash = md.digest(b); + } + */ + + baseStream.write(hash); + + } else { + // Write 32-bits of zeros + baseStream.writeLong(0L); + baseStream.writeLong(0L); + baseStream.writeLong(0L); + baseStream.writeLong(0L); + } + + baseStream.writeInt(bufferPos); + + if ( bufferPos > 0 ) { + baseStream.write(buffer, 0, bufferPos); + } + + bufferPos = 0; + + } + + @Override + public void write(byte[] buffer) throws IOException { + write(buffer, 0, buffer.length); + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/LEDataInputStream.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/LEDataInputStream.java new file mode 100644 index 00000000..c96c7898 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/LEDataInputStream.java @@ -0,0 +1,184 @@ +/* + * Copyright 2010-2011 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.stream; + +import java.io.IOException; +import java.io.InputStream; + + +/** Little endian version of the DataInputStream + * @author bpellin + * + */ +public class LEDataInputStream extends InputStream { + + public static final long INT_TO_LONG_MASK = 0xffffffffL; + + private InputStream baseStream; + + public LEDataInputStream(InputStream in) { + baseStream = in; + } + + /** Read a 32-bit value and return it as a long, so that it can + * be interpreted as an unsigned integer. + * @return + * @throws IOException + */ + public long readUInt() throws IOException { + return readUInt(baseStream); + } + + public int readInt() throws IOException { + return readInt(baseStream); + } + + public long readLong() throws IOException { + byte[] buf = readBytes(8); + + return readLong(buf, 0); + } + + @Override + public int available() throws IOException { + return baseStream.available(); + } + + @Override + public void close() throws IOException { + baseStream.close(); + } + + @Override + public void mark(int readlimit) { + baseStream.mark(readlimit); + } + + @Override + public boolean markSupported() { + return baseStream.markSupported(); + } + + @Override + public int read() throws IOException { + return baseStream.read(); + } + + @Override + public int read(byte[] b, int offset, int length) throws IOException { + return baseStream.read(b, offset, length); + } + + @Override + public int read(byte[] b) throws IOException { + // TODO Auto-generated method stub + return super.read(b); + } + + @Override + public synchronized void reset() throws IOException { + baseStream.reset(); + } + + @Override + public long skip(long n) throws IOException { + return baseStream.skip(n); + } + + public byte[] readBytes(int length) throws IOException { + byte[] buf = new byte[length]; + + int count = 0; + while ( count < length ) { + int read = read(buf, count, length - count); + + // Reached end + if ( read == -1 ) { + // Stop early + byte[] early = new byte[count]; + System.arraycopy(buf, 0, early, 0, count); + return early; + } + + count += read; + } + + return buf; + } + + public static int readUShort(InputStream is) throws IOException { + byte[] buf = new byte[2]; + + is.read(buf, 0, 2); + + return readUShort(buf, 0); + } + + public int readUShort() throws IOException { + return readUShort(baseStream); + } + + /** + * Read an unsigned 16-bit value. + * + * @param buf + * @param offset + * @return + */ + public static int readUShort( byte[] buf, int offset ) { + return (buf[offset + 0] & 0xFF) + ((buf[offset + 1] & 0xFF) << 8); + } + + public static long readLong( byte buf[], int offset ) { + return ((long)buf[offset + 0] & 0xFF) + (((long)buf[offset + 1] & 0xFF) << 8) + + (((long)buf[offset + 2] & 0xFF) << 16) + (((long)buf[offset + 3] & 0xFF) << 24) + + (((long)buf[offset + 4] & 0xFF) << 32) + (((long)buf[offset + 5] & 0xFF) << 40) + + (((long)buf[offset + 6] & 0xFF) << 48) + (((long)buf[offset + 7] & 0xFF) << 56); + } + + public static long readUInt( byte buf[], int offset ) { + return (readInt(buf, offset) & INT_TO_LONG_MASK); + } + + public static int readInt(InputStream is) throws IOException { + byte[] buf = new byte[4]; + + is.read(buf, 0, 4); + + return readInt(buf, 0); + } + + public static long readUInt(InputStream is) throws IOException { + return (readInt(is) & INT_TO_LONG_MASK); + } + + /** + * Read a 32-bit value. + * + * @param buf + * @param offset + * @return + */ + public static int readInt( byte buf[], int offset ) { + return (buf[offset + 0] & 0xFF) + ((buf[offset + 1] & 0xFF) << 8) + ((buf[offset + 2] & 0xFF) << 16) + + ((buf[offset + 3] & 0xFF) << 24); + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/LEDataOutputStream.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/LEDataOutputStream.java new file mode 100644 index 00000000..2ad39031 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/LEDataOutputStream.java @@ -0,0 +1,144 @@ +/* + * Copyright 2010 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.stream; + +import java.io.IOException; +import java.io.OutputStream; + + +/** Little Endian version of the DataOutputStream + * @author bpellin + * + */ +public class LEDataOutputStream extends OutputStream { + + private OutputStream baseStream; + + public LEDataOutputStream(OutputStream out) { + baseStream = out; + } + + public void writeUInt(long uint) throws IOException { + baseStream.write(LEDataOutputStream.writeIntBuf((int) uint)); + } + + @Override + public void close() throws IOException { + baseStream.close(); + } + + @Override + public void flush() throws IOException { + baseStream.flush(); + } + + @Override + public void write(byte[] buffer, int offset, int count) throws IOException { + baseStream.write(buffer, offset, count); + } + + @Override + public void write(byte[] buffer) throws IOException { + baseStream.write(buffer); + } + + @Override + public void write(int oneByte) throws IOException { + baseStream.write(oneByte); + } + + public void writeLong(long val) throws IOException { + byte[] buf = new byte[8]; + + writeLong(val, buf, 0); + baseStream.write(buf); + } + + public void writeInt(int val) throws IOException { + byte[] buf = new byte[4]; + writeInt(val, buf, 0); + + baseStream.write(buf); + } + + public void writeUShort(int val) throws IOException { + byte[] buf = new byte[2]; + writeUShort(val, buf, 0); + baseStream.write(buf); + } + + public static byte[] writeIntBuf(int val) { + byte[] buf = new byte[4]; + writeInt(val, buf, 0); + + return buf; + } + + public static byte[] writeUShortBuf(int val) { + byte[] buf = new byte[2]; + + writeUShort(val, buf, 0); + + return buf; + } + + /** Write an unsigned 16-bit value + * + * @param val + * @param buf + * @param offset + */ + public static void writeUShort(int val, byte[] buf, int offset) { + buf[offset + 0] = (byte)(val & 0x00FF); + buf[offset + 1] = (byte)((val & 0xFF00) >> 8); + } + + /** + * Write a 32-bit value. + * + * @param val + * @param buf + * @param offset + */ + public static void writeInt( int val, byte[] buf, int offset ) { + buf[offset + 0] = (byte)(val & 0xFF); + buf[offset + 1] = (byte)((val >>> 8) & 0xFF); + buf[offset + 2] = (byte)((val >>> 16) & 0xFF); + buf[offset + 3] = (byte)((val >>> 24) & 0xFF); + } + + public static byte[] writeLongBuf(long val) { + byte[] buf = new byte[8]; + writeLong(val, buf, 0); + return buf; + } + + public static void writeLong( long val, byte[] buf, int offset ) { + buf[offset + 0] = (byte)(val & 0xFF); + buf[offset + 1] = (byte)((val >>> 8) & 0xFF); + buf[offset + 2] = (byte)((val >>> 16) & 0xFF); + buf[offset + 3] = (byte)((val >>> 24) & 0xFF); + buf[offset + 4] = (byte)((val >>> 32) & 0xFF); + buf[offset + 5] = (byte)((val >>> 40) & 0xFF); + buf[offset + 6] = (byte)((val >>> 48) & 0xFF); + buf[offset + 7] = (byte)((val >>> 56) & 0xFF); + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/NullOutputStream.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/NullOutputStream.java new file mode 100644 index 00000000..a8127695 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/NullOutputStream.java @@ -0,0 +1,51 @@ +/* + * Copyright 2009 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.stream; + +import java.io.IOException; +import java.io.OutputStream; + +public class NullOutputStream extends OutputStream { + + @Override + public void close() throws IOException { + super.close(); + } + + @Override + public void flush() throws IOException { + super.flush(); + } + + @Override + public void write(byte[] buffer, int offset, int count) throws IOException { + super.write(buffer, offset, count); + } + + @Override + public void write(byte[] buffer) throws IOException { + super.write(buffer); + } + + @Override + public void write(int oneByte) throws IOException { + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/RandomFileOutputStream.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/RandomFileOutputStream.java new file mode 100644 index 00000000..ed973d29 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/stream/RandomFileOutputStream.java @@ -0,0 +1,64 @@ +/* + * Copyright 2009 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + */ +package com.keepassdroid.stream; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.RandomAccessFile; + +public class RandomFileOutputStream extends OutputStream { + + RandomAccessFile mFile; + + RandomFileOutputStream(RandomAccessFile file) { + mFile = file; + } + + @Override + public void close() throws IOException { + super.close(); + + mFile.close(); + } + + @Override + public void write(byte[] buffer, int offset, int count) throws IOException { + super.write(buffer, offset, count); + + mFile.write(buffer, offset, count); + } + + @Override + public void write(byte[] buffer) throws IOException { + super.write(buffer); + + mFile.write(buffer); + } + + @Override + public void write(int oneByte) throws IOException { + mFile.write(oneByte); + } + + public void seek(long pos) throws IOException { + mFile.seek(pos); + } + +} diff --git a/src/java/KP2AKdbLibrary/src/com/keepassdroid/utils/Types.java b/src/java/KP2AKdbLibrary/src/com/keepassdroid/utils/Types.java new file mode 100644 index 00000000..b7cf957f --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/com/keepassdroid/utils/Types.java @@ -0,0 +1,180 @@ +/* + * Copyright 2009-2011 Brian Pellin. + * + * This file is part of KeePassDroid. + * + * KeePassDroid 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. + * + * KeePassDroid 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 KeePassDroid. If not, see . + * + * +KeePass for J2ME + +Copyright 2007 Naomaru Itoi + +This file was derived from + +Java clone of KeePass - A KeePass file viewer for Java +Copyright 2006 Bill Zwicky + +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; version 2 + +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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +package com.keepassdroid.utils; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.util.UUID; + +import com.keepassdroid.stream.LEDataOutputStream; + + +/** + * Tools for slicing and dicing Java and KeePass data types. + * + * @author Bill Zwicky + */ +public class Types { + + /** Read an unsigned byte */ + public static int readUByte( byte[] buf, int offset ) { + return ((int)buf[offset] & 0xFF); + } + + /** Write an unsigned byte + * + * @param val + * @param buf + * @param offset + */ + public static void writeUByte(int val, byte[] buf, int offset) { + buf[offset] = (byte)(val & 0xFF); + } + + public static byte writeUByte(int val) { + byte[] buf = new byte[1]; + + writeUByte(val, buf, 0); + + return buf[0]; + } + + /** + * Return len of null-terminated string (i.e. distance to null) + * within a byte buffer. + * + * @param buf + * @param offset + * @return + */ + public static int strlen( byte[] buf, int offset ) { + int len = 0; + while( buf[offset + len] != 0 ) + len++; + return len; + } + + + + /** + * Copy a sequence of bytes into a new array. + * + * @param b - source array + * @param offset - first byte + * @param len - number of bytes + * @return new byte[len] + */ + public static byte[] extract( byte[] b, int offset, int len ) { + byte[] b2 = new byte[len]; + System.arraycopy( b, offset, b2, 0, len ); + return b2; + } + + + private static final byte[] CRLFbuf = { 0x0D, 0x0A }; + private static final String CRLF = new String(CRLFbuf); + private static final String SEP = System.getProperty("line.separator"); + private static final boolean REPLACE = ! SEP.equals(CRLF); + + public static String readCString(byte[] buf, int offset) throws UnsupportedEncodingException { + String jstring = new String(buf, offset, strlen(buf, offset), "UTF-8"); + + if ( REPLACE ) { + jstring = jstring.replace(CRLF, SEP); + } + + return jstring; + } + + public static int writeCString(String str, OutputStream os) throws IOException { + if ( str == null ) { + // Write out a null character + os.write(LEDataOutputStream.writeIntBuf(1)); + os.write(0x00); + return 0; + } + + if ( REPLACE ) { + str = str.replace(SEP, CRLF); + } + + byte[] initial = str.getBytes("UTF-8"); + + int length = initial.length+1; + os.write(LEDataOutputStream.writeIntBuf(length)); + os.write(initial); + os.write(0x00); + + return length; + } + + public static UUID bytestoUUID(byte[] buf) { + return bytestoUUID(buf, 0); + } + + public static UUID bytestoUUID(byte[] buf, int offset) { + long lsb = 0; + for (int i = 15; i >= 8; i--) { + lsb = (lsb << 8) | (buf[i + offset] & 0xff); + } + + long msb = 0; + for (int i = 7; i >= 0; i--) { + msb = (msb << 8) | (buf[i + offset] & 0xff); + } + + return new UUID(msb, lsb); + + } + + public static byte[] UUIDtoBytes(UUID uuid) { + byte[] buf = new byte[16]; + + LEDataOutputStream.writeLong(uuid.getMostSignificantBits(), buf, 0); + LEDataOutputStream.writeLong(uuid.getLeastSignificantBits(), buf, 8); + + return buf; + } + +} diff --git a/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/IterableMap.java b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/IterableMap.java new file mode 100644 index 00000000..e0f5d236 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/IterableMap.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.collections; + +import java.util.Map; + +/** + * Defines a map that can be iterated directly without needing to create an entry set. + *

+ * A map iterator is an efficient way of iterating over maps. + * There is no need to access the entry set or cast to Map Entry objects. + *

+ * IterableMap map = new HashedMap();
+ * MapIterator it = map.mapIterator();
+ * while (it.hasNext()) {
+ *   Object key = it.next();
+ *   Object value = it.getValue();
+ *   it.setValue("newValue");
+ * }
+ * 
+ * + * @since Commons Collections 3.0 + * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ + * + * @author Stephen Colebourne + */ +public interface IterableMap extends Map { + + /** + * Obtains a MapIterator over the map. + *

+ * A map iterator is an efficient way of iterating over maps. + * There is no need to access the entry set or cast to Map Entry objects. + *

+     * IterableMap map = new HashedMap();
+     * MapIterator it = map.mapIterator();
+     * while (it.hasNext()) {
+     *   Object key = it.next();
+     *   Object value = it.getValue();
+     *   it.setValue("newValue");
+     * }
+     * 
+ * + * @return a map iterator + */ + MapIterator mapIterator(); + +} diff --git a/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/KeyValue.java b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/KeyValue.java new file mode 100644 index 00000000..ece1f85c --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/KeyValue.java @@ -0,0 +1,47 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.collections; + +/** + * Defines a simple key value pair. + *

+ * A Map Entry has considerable additional semantics over and above a simple + * key-value pair. This interface defines the minimum key value, with just the + * two get methods. + * + * @since Commons Collections 3.0 + * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ + * + * @author Stephen Colebourne + */ +public interface KeyValue { + + /** + * Gets the key from the pair. + * + * @return the key + */ + Object getKey(); + + /** + * Gets the value from the pair. + * + * @return the value + */ + Object getValue(); + +} diff --git a/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/MapIterator.java b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/MapIterator.java new file mode 100644 index 00000000..cc100e97 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/MapIterator.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.collections; + +import java.util.Iterator; + +/** + * Defines an iterator that operates over a Map. + *

+ * This iterator is a special version designed for maps. It can be more + * efficient to use this rather than an entry set iterator where the option + * is available, and it is certainly more convenient. + *

+ * A map that provides this interface may not hold the data internally using + * Map Entry objects, thus this interface can avoid lots of object creation. + *

+ * In use, this iterator iterates through the keys in the map. After each call + * to next(), the getValue() method provides direct + * access to the value. The value can also be set using setValue(). + *

+ * MapIterator it = map.mapIterator();
+ * while (it.hasNext()) {
+ *   Object key = it.next();
+ *   Object value = it.getValue();
+ *   it.setValue(newValue);
+ * }
+ * 
+ * + * @since Commons Collections 3.0 + * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ + * + * @author Stephen Colebourne + */ +public interface MapIterator extends Iterator { + + /** + * Checks to see if there are more entries still to be iterated. + * + * @return true if the iterator has more elements + */ + boolean hasNext(); + + /** + * Gets the next key from the Map. + * + * @return the next key in the iteration + * @throws java.util.NoSuchElementException if the iteration is finished + */ + Object next(); + + //----------------------------------------------------------------------- + /** + * Gets the current key, which is the key returned by the last call + * to next(). + * + * @return the current key + * @throws IllegalStateException if next() has not yet been called + */ + Object getKey(); + + /** + * Gets the current value, which is the value associated with the last key + * returned by next(). + * + * @return the current value + * @throws IllegalStateException if next() has not yet been called + */ + Object getValue(); + + //----------------------------------------------------------------------- + /** + * Removes the last returned key from the underlying Map (optional operation). + *

+ * This method can be called once per call to next(). + * + * @throws UnsupportedOperationException if remove is not supported by the map + * @throws IllegalStateException if next() has not yet been called + * @throws IllegalStateException if remove() has already been called + * since the last call to next() + */ + void remove(); + + /** + * Sets the value associated with the current key (optional operation). + * + * @param value the new value + * @return the previous value + * @throws UnsupportedOperationException if setValue is not supported by the map + * @throws IllegalStateException if next() has not yet been called + * @throws IllegalStateException if remove() has been called since the + * last call to next() + */ + Object setValue(Object value); + +} diff --git a/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/ResettableIterator.java b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/ResettableIterator.java new file mode 100644 index 00000000..90c62577 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/ResettableIterator.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.collections; + +import java.util.Iterator; + +/** + * Defines an iterator that can be reset back to an initial state. + *

+ * This interface allows an iterator to be repeatedly reused. + * + * @since Commons Collections 3.0 + * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ + * + * @author Stephen Colebourne + */ +public interface ResettableIterator extends Iterator { + + /** + * Resets the iterator back to the position at which the iterator + * was created. + */ + public void reset(); + +} diff --git a/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/iterators/AbstractEmptyIterator.java b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/iterators/AbstractEmptyIterator.java new file mode 100644 index 00000000..1d90ad59 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/iterators/AbstractEmptyIterator.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.collections.iterators; + +import java.util.NoSuchElementException; + +/** + * Provides an implementation of an empty iterator. + * + * @since Commons Collections 3.1 + * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ + * + * @author Stephen Colebourne + */ +abstract class AbstractEmptyIterator { + + /** + * Constructor. + */ + protected AbstractEmptyIterator() { + super(); + } + + public boolean hasNext() { + return false; + } + + public Object next() { + throw new NoSuchElementException("Iterator contains no elements"); + } + + public boolean hasPrevious() { + return false; + } + + public Object previous() { + throw new NoSuchElementException("Iterator contains no elements"); + } + + public int nextIndex() { + return 0; + } + + public int previousIndex() { + return -1; + } + + public void add(Object obj) { + throw new UnsupportedOperationException("add() not supported for empty Iterator"); + } + + public void set(Object obj) { + throw new IllegalStateException("Iterator contains no elements"); + } + + public void remove() { + throw new IllegalStateException("Iterator contains no elements"); + } + + public Object getKey() { + throw new IllegalStateException("Iterator contains no elements"); + } + + public Object getValue() { + throw new IllegalStateException("Iterator contains no elements"); + } + + public Object setValue(Object value) { + throw new IllegalStateException("Iterator contains no elements"); + } + + public void reset() { + // do nothing + } + +} diff --git a/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/iterators/EmptyIterator.java b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/iterators/EmptyIterator.java new file mode 100644 index 00000000..1685bfb6 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/iterators/EmptyIterator.java @@ -0,0 +1,55 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.collections.iterators; + +import java.util.Iterator; + +import org.apache.commons.collections.ResettableIterator; + +/** + * Provides an implementation of an empty iterator. + *

+ * This class provides an implementation of an empty iterator. + * This class provides for binary compatability between Commons Collections + * 2.1.1 and 3.1 due to issues with IteratorUtils. + * + * @since Commons Collections 2.1.1 and 3.1 + * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ + * + * @author Stephen Colebourne + */ +public class EmptyIterator extends AbstractEmptyIterator implements ResettableIterator { + + /** + * Singleton instance of the iterator. + * @since Commons Collections 3.1 + */ + public static final ResettableIterator RESETTABLE_INSTANCE = new EmptyIterator(); + /** + * Singleton instance of the iterator. + * @since Commons Collections 2.1.1 and 3.1 + */ + public static final Iterator INSTANCE = RESETTABLE_INSTANCE; + + /** + * Constructor. + */ + protected EmptyIterator() { + super(); + } + +} diff --git a/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/iterators/EmptyMapIterator.java b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/iterators/EmptyMapIterator.java new file mode 100644 index 00000000..011ab295 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/iterators/EmptyMapIterator.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.collections.iterators; + +import org.apache.commons.collections.MapIterator; +import org.apache.commons.collections.ResettableIterator; + +/** + * Provides an implementation of an empty map iterator. + * + * @since Commons Collections 3.1 + * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ + * + * @author Stephen Colebourne + */ +public class EmptyMapIterator extends AbstractEmptyIterator implements MapIterator, ResettableIterator { + + /** + * Singleton instance of the iterator. + * @since Commons Collections 3.1 + */ + public static final MapIterator INSTANCE = new EmptyMapIterator(); + + /** + * Constructor. + */ + protected EmptyMapIterator() { + super(); + } + +} diff --git a/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/keyvalue/AbstractKeyValue.java b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/keyvalue/AbstractKeyValue.java new file mode 100644 index 00000000..02702571 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/keyvalue/AbstractKeyValue.java @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.collections.keyvalue; + +import org.apache.commons.collections.KeyValue; + +/** + * Abstract pair class to assist with creating KeyValue + * and {@link java.util.Map.Entry Map.Entry} implementations. + * + * @since Commons Collections 3.0 + * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ + * + * @author James Strachan + * @author Michael A. Smith + * @author Neil O'Toole + * @author Stephen Colebourne + */ +public abstract class AbstractKeyValue implements KeyValue { + + /** The key */ + protected Object key; + /** The value */ + protected Object value; + + /** + * Constructs a new pair with the specified key and given value. + * + * @param key the key for the entry, may be null + * @param value the value for the entry, may be null + */ + protected AbstractKeyValue(Object key, Object value) { + super(); + this.key = key; + this.value = value; + } + + /** + * Gets the key from the pair. + * + * @return the key + */ + public Object getKey() { + return key; + } + + /** + * Gets the value from the pair. + * + * @return the value + */ + public Object getValue() { + return value; + } + + /** + * Gets a debugging String view of the pair. + * + * @return a String view of the entry + */ + public String toString() { + return new StringBuffer() + .append(getKey()) + .append('=') + .append(getValue()) + .toString(); + } + +} diff --git a/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/keyvalue/AbstractMapEntry.java b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/keyvalue/AbstractMapEntry.java new file mode 100644 index 00000000..14e1f9e9 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/keyvalue/AbstractMapEntry.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.collections.keyvalue; + +import java.util.Map; + +/** + * Abstract Pair class to assist with creating correct + * {@link java.util.Map.Entry Map.Entry} implementations. + * + * @since Commons Collections 3.0 + * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ + * + * @author James Strachan + * @author Michael A. Smith + * @author Neil O'Toole + * @author Stephen Colebourne + */ +public abstract class AbstractMapEntry extends AbstractKeyValue implements Map.Entry { + + /** + * Constructs a new entry with the given key and given value. + * + * @param key the key for the entry, may be null + * @param value the value for the entry, may be null + */ + protected AbstractMapEntry(Object key, Object value) { + super(key, value); + } + + // Map.Entry interface + //------------------------------------------------------------------------- + /** + * Sets the value stored in this Map.Entry. + *

+ * This Map.Entry is not connected to a Map, so only the + * local data is changed. + * + * @param value the new value + * @return the previous value + */ + public Object setValue(Object value) { + Object answer = this.value; + this.value = value; + return answer; + } + + /** + * Compares this Map.Entry with another Map.Entry. + *

+ * Implemented per API documentation of {@link java.util.Map.Entry#equals(Object)} + * + * @param obj the object to compare to + * @return true if equal key and value + */ + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof Map.Entry == false) { + return false; + } + Map.Entry other = (Map.Entry) obj; + return + (getKey() == null ? other.getKey() == null : getKey().equals(other.getKey())) && + (getValue() == null ? other.getValue() == null : getValue().equals(other.getValue())); + } + + /** + * Gets a hashCode compatible with the equals method. + *

+ * Implemented per API documentation of {@link java.util.Map.Entry#hashCode()} + * + * @return a suitable hash code + */ + public int hashCode() { + return (getKey() == null ? 0 : getKey().hashCode()) ^ + (getValue() == null ? 0 : getValue().hashCode()); + } + +} diff --git a/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/keyvalue/DefaultMapEntry.java b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/keyvalue/DefaultMapEntry.java new file mode 100644 index 00000000..cff540b7 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/keyvalue/DefaultMapEntry.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.collections.keyvalue; + +import java.util.Map; + +import org.apache.commons.collections.KeyValue; + +/** + * A restricted implementation of {@link java.util.Map.Entry} that prevents + * the Map.Entry contract from being broken. + * + * @since Commons Collections 3.0 + * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ + * + * @author James Strachan + * @author Michael A. Smith + * @author Neil O'Toole + * @author Stephen Colebourne + */ +public final class DefaultMapEntry extends AbstractMapEntry { + + /** + * Constructs a new entry with the specified key and given value. + * + * @param key the key for the entry, may be null + * @param value the value for the entry, may be null + */ + public DefaultMapEntry(final Object key, final Object value) { + super(key, value); + } + + /** + * Constructs a new entry from the specified KeyValue. + * + * @param pair the pair to copy, must not be null + * @throws NullPointerException if the entry is null + */ + public DefaultMapEntry(final KeyValue pair) { + super(pair.getKey(), pair.getValue()); + } + + /** + * Constructs a new entry from the specified Map.Entry. + * + * @param entry the entry to copy, must not be null + * @throws NullPointerException if the entry is null + */ + public DefaultMapEntry(final Map.Entry entry) { + super(entry.getKey(), entry.getValue()); + } + +} diff --git a/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/map/AbstractHashedMap.java b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/map/AbstractHashedMap.java new file mode 100644 index 00000000..1e0e46c2 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/map/AbstractHashedMap.java @@ -0,0 +1,1329 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.collections.map; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.util.AbstractCollection; +import java.util.AbstractMap; +import java.util.AbstractSet; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +import org.apache.commons.collections.IterableMap; +import org.apache.commons.collections.KeyValue; +import org.apache.commons.collections.MapIterator; +import org.apache.commons.collections.iterators.EmptyIterator; +import org.apache.commons.collections.iterators.EmptyMapIterator; + +/** + * An abstract implementation of a hash-based map which provides numerous points for + * subclasses to override. + *

+ * This class implements all the features necessary for a subclass hash-based map. + * Key-value entries are stored in instances of the HashEntry class, + * which can be overridden and replaced. The iterators can similarly be replaced, + * without the need to replace the KeySet, EntrySet and Values view classes. + *

+ * Overridable methods are provided to change the default hashing behaviour, and + * to change how entries are added to and removed from the map. Hopefully, all you + * need for unusual subclasses is here. + *

+ * NOTE: From Commons Collections 3.1 this class extends AbstractMap. + * This is to provide backwards compatibility for ReferenceMap between v3.0 and v3.1. + * This extends clause will be removed in v4.0. + * + * @since Commons Collections 3.0 + * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ + * + * @author java util HashMap + * @author Stephen Colebourne + * @author Christian Siefkes + */ +public class AbstractHashedMap extends AbstractMap implements IterableMap { + + protected static final String NO_NEXT_ENTRY = "No next() entry in the iteration"; + protected static final String NO_PREVIOUS_ENTRY = "No previous() entry in the iteration"; + protected static final String REMOVE_INVALID = "remove() can only be called once after next()"; + protected static final String GETKEY_INVALID = "getKey() can only be called after next() and before remove()"; + protected static final String GETVALUE_INVALID = "getValue() can only be called after next() and before remove()"; + protected static final String SETVALUE_INVALID = "setValue() can only be called after next() and before remove()"; + + /** The default capacity to use */ + protected static final int DEFAULT_CAPACITY = 16; + /** The default threshold to use */ + protected static final int DEFAULT_THRESHOLD = 12; + /** The default load factor to use */ + protected static final float DEFAULT_LOAD_FACTOR = 0.75f; + /** The maximum capacity allowed */ + protected static final int MAXIMUM_CAPACITY = 1 << 30; + /** An object for masking null */ + protected static final Object NULL = new Object(); + + /** Load factor, normally 0.75 */ + protected transient float loadFactor; + /** The size of the map */ + protected transient int size; + /** Map entries */ + protected transient HashEntry[] data; + /** Size at which to rehash */ + protected transient int threshold; + /** Modification count for iterators */ + protected transient int modCount; + /** Entry set */ + protected transient EntrySet entrySet; + /** Key set */ + protected transient KeySet keySet; + /** Values */ + protected transient Values values; + + /** + * Constructor only used in deserialization, do not use otherwise. + */ + protected AbstractHashedMap() { + super(); + } + + /** + * Constructor which performs no validation on the passed in parameters. + * + * @param initialCapacity the initial capacity, must be a power of two + * @param loadFactor the load factor, must be > 0.0f and generally < 1.0f + * @param threshold the threshold, must be sensible + */ + protected AbstractHashedMap(int initialCapacity, float loadFactor, int threshold) { + super(); + this.loadFactor = loadFactor; + this.data = new HashEntry[initialCapacity]; + this.threshold = threshold; + init(); + } + + /** + * Constructs a new, empty map with the specified initial capacity and + * default load factor. + * + * @param initialCapacity the initial capacity + * @throws IllegalArgumentException if the initial capacity is less than one + */ + protected AbstractHashedMap(int initialCapacity) { + this(initialCapacity, DEFAULT_LOAD_FACTOR); + } + + /** + * Constructs a new, empty map with the specified initial capacity and + * load factor. + * + * @param initialCapacity the initial capacity + * @param loadFactor the load factor + * @throws IllegalArgumentException if the initial capacity is less than one + * @throws IllegalArgumentException if the load factor is less than or equal to zero + */ + protected AbstractHashedMap(int initialCapacity, float loadFactor) { + super(); + if (initialCapacity < 1) { + throw new IllegalArgumentException("Initial capacity must be greater than 0"); + } + if (loadFactor <= 0.0f || Float.isNaN(loadFactor)) { + throw new IllegalArgumentException("Load factor must be greater than 0"); + } + this.loadFactor = loadFactor; + initialCapacity = calculateNewCapacity(initialCapacity); + this.threshold = calculateThreshold(initialCapacity, loadFactor); + this.data = new HashEntry[initialCapacity]; + init(); + } + + /** + * Constructor copying elements from another map. + * + * @param map the map to copy + * @throws NullPointerException if the map is null + */ + protected AbstractHashedMap(Map map) { + this(Math.max(2 * map.size(), DEFAULT_CAPACITY), DEFAULT_LOAD_FACTOR); + putAll(map); + } + + /** + * Initialise subclasses during construction, cloning or deserialization. + */ + protected void init() { + } + + //----------------------------------------------------------------------- + /** + * Gets the value mapped to the key specified. + * + * @param key the key + * @return the mapped value, null if no match + */ + public Object get(Object key) { + key = convertKey(key); + int hashCode = hash(key); + HashEntry entry = data[hashIndex(hashCode, data.length)]; // no local for hash index + while (entry != null) { + if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) { + return entry.getValue(); + } + entry = entry.next; + } + return null; + } + + /** + * Gets the size of the map. + * + * @return the size + */ + public int size() { + return size; + } + + /** + * Checks whether the map is currently empty. + * + * @return true if the map is currently size zero + */ + public boolean isEmpty() { + return (size == 0); + } + + //----------------------------------------------------------------------- + /** + * Checks whether the map contains the specified key. + * + * @param key the key to search for + * @return true if the map contains the key + */ + public boolean containsKey(Object key) { + key = convertKey(key); + int hashCode = hash(key); + HashEntry entry = data[hashIndex(hashCode, data.length)]; // no local for hash index + while (entry != null) { + if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) { + return true; + } + entry = entry.next; + } + return false; + } + + /** + * Checks whether the map contains the specified value. + * + * @param value the value to search for + * @return true if the map contains the value + */ + public boolean containsValue(Object value) { + if (value == null) { + for (int i = 0, isize = data.length; i < isize; i++) { + HashEntry entry = data[i]; + while (entry != null) { + if (entry.getValue() == null) { + return true; + } + entry = entry.next; + } + } + } else { + for (int i = 0, isize = data.length; i < isize; i++) { + HashEntry entry = data[i]; + while (entry != null) { + if (isEqualValue(value, entry.getValue())) { + return true; + } + entry = entry.next; + } + } + } + return false; + } + + //----------------------------------------------------------------------- + /** + * Puts a key-value mapping into this map. + * + * @param key the key to add + * @param value the value to add + * @return the value previously mapped to this key, null if none + */ + public Object put(Object key, Object value) { + key = convertKey(key); + int hashCode = hash(key); + int index = hashIndex(hashCode, data.length); + HashEntry entry = data[index]; + while (entry != null) { + if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) { + Object oldValue = entry.getValue(); + updateEntry(entry, value); + return oldValue; + } + entry = entry.next; + } + + addMapping(index, hashCode, key, value); + return null; + } + + /** + * Puts all the values from the specified map into this map. + *

+ * This implementation iterates around the specified map and + * uses {@link #put(Object, Object)}. + * + * @param map the map to add + * @throws NullPointerException if the map is null + */ + public void putAll(Map map) { + int mapSize = map.size(); + if (mapSize == 0) { + return; + } + int newSize = (int) ((size + mapSize) / loadFactor + 1); + ensureCapacity(calculateNewCapacity(newSize)); + for (Iterator it = map.entrySet().iterator(); it.hasNext();) { + Map.Entry entry = (Map.Entry) it.next(); + put(entry.getKey(), entry.getValue()); + } + } + + /** + * Removes the specified mapping from this map. + * + * @param key the mapping to remove + * @return the value mapped to the removed key, null if key not in map + */ + public Object remove(Object key) { + key = convertKey(key); + int hashCode = hash(key); + int index = hashIndex(hashCode, data.length); + HashEntry entry = data[index]; + HashEntry previous = null; + while (entry != null) { + if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) { + Object oldValue = entry.getValue(); + removeMapping(entry, index, previous); + return oldValue; + } + previous = entry; + entry = entry.next; + } + return null; + } + + /** + * Clears the map, resetting the size to zero and nullifying references + * to avoid garbage collection issues. + */ + public void clear() { + modCount++; + HashEntry[] data = this.data; + for (int i = data.length - 1; i >= 0; i--) { + data[i] = null; + } + size = 0; + } + + //----------------------------------------------------------------------- + /** + * Converts input keys to another object for storage in the map. + * This implementation masks nulls. + * Subclasses can override this to perform alternate key conversions. + *

+ * The reverse conversion can be changed, if required, by overriding the + * getKey() method in the hash entry. + * + * @param key the key convert + * @return the converted key + */ + protected Object convertKey(Object key) { + return (key == null ? NULL : key); + } + + /** + * Gets the hash code for the key specified. + * This implementation uses the additional hashing routine from JDK1.4. + * Subclasses can override this to return alternate hash codes. + * + * @param key the key to get a hash code for + * @return the hash code + */ + protected int hash(Object key) { + // same as JDK 1.4 + int h = key.hashCode(); + h += ~(h << 9); + h ^= (h >>> 14); + h += (h << 4); + h ^= (h >>> 10); + return h; + } + + /** + * Compares two keys, in internal converted form, to see if they are equal. + * This implementation uses the equals method and assumes neither key is null. + * Subclasses can override this to match differently. + * + * @param key1 the first key to compare passed in from outside + * @param key2 the second key extracted from the entry via entry.key + * @return true if equal + */ + protected boolean isEqualKey(Object key1, Object key2) { + return (key1 == key2 || key1.equals(key2)); + } + + /** + * Compares two values, in external form, to see if they are equal. + * This implementation uses the equals method and assumes neither value is null. + * Subclasses can override this to match differently. + * + * @param value1 the first value to compare passed in from outside + * @param value2 the second value extracted from the entry via getValue() + * @return true if equal + */ + protected boolean isEqualValue(Object value1, Object value2) { + return (value1 == value2 || value1.equals(value2)); + } + + /** + * Gets the index into the data storage for the hashCode specified. + * This implementation uses the least significant bits of the hashCode. + * Subclasses can override this to return alternate bucketing. + * + * @param hashCode the hash code to use + * @param dataSize the size of the data to pick a bucket from + * @return the bucket index + */ + protected int hashIndex(int hashCode, int dataSize) { + return hashCode & (dataSize - 1); + } + + //----------------------------------------------------------------------- + /** + * Gets the entry mapped to the key specified. + *

+ * This method exists for subclasses that may need to perform a multi-step + * process accessing the entry. The public methods in this class don't use this + * method to gain a small performance boost. + * + * @param key the key + * @return the entry, null if no match + */ + protected HashEntry getEntry(Object key) { + key = convertKey(key); + int hashCode = hash(key); + HashEntry entry = data[hashIndex(hashCode, data.length)]; // no local for hash index + while (entry != null) { + if (entry.hashCode == hashCode && isEqualKey(key, entry.key)) { + return entry; + } + entry = entry.next; + } + return null; + } + + //----------------------------------------------------------------------- + /** + * Updates an existing key-value mapping to change the value. + *

+ * This implementation calls setValue() on the entry. + * Subclasses could override to handle changes to the map. + * + * @param entry the entry to update + * @param newValue the new value to store + */ + protected void updateEntry(HashEntry entry, Object newValue) { + entry.setValue(newValue); + } + + /** + * Reuses an existing key-value mapping, storing completely new data. + *

+ * This implementation sets all the data fields on the entry. + * Subclasses could populate additional entry fields. + * + * @param entry the entry to update, not null + * @param hashIndex the index in the data array + * @param hashCode the hash code of the key to add + * @param key the key to add + * @param value the value to add + */ + protected void reuseEntry(HashEntry entry, int hashIndex, int hashCode, Object key, Object value) { + entry.next = data[hashIndex]; + entry.hashCode = hashCode; + entry.key = key; + entry.value = value; + } + + //----------------------------------------------------------------------- + /** + * Adds a new key-value mapping into this map. + *

+ * This implementation calls createEntry(), addEntry() + * and checkCapacity(). + * It also handles changes to modCount and size. + * Subclasses could override to fully control adds to the map. + * + * @param hashIndex the index into the data array to store at + * @param hashCode the hash code of the key to add + * @param key the key to add + * @param value the value to add + */ + protected void addMapping(int hashIndex, int hashCode, Object key, Object value) { + modCount++; + HashEntry entry = createEntry(data[hashIndex], hashCode, key, value); + addEntry(entry, hashIndex); + size++; + checkCapacity(); + } + + /** + * Creates an entry to store the key-value data. + *

+ * This implementation creates a new HashEntry instance. + * Subclasses can override this to return a different storage class, + * or implement caching. + * + * @param next the next entry in sequence + * @param hashCode the hash code to use + * @param key the key to store + * @param value the value to store + * @return the newly created entry + */ + protected HashEntry createEntry(HashEntry next, int hashCode, Object key, Object value) { + return new HashEntry(next, hashCode, key, value); + } + + /** + * Adds an entry into this map. + *

+ * This implementation adds the entry to the data storage table. + * Subclasses could override to handle changes to the map. + * + * @param entry the entry to add + * @param hashIndex the index into the data array to store at + */ + protected void addEntry(HashEntry entry, int hashIndex) { + data[hashIndex] = entry; + } + + //----------------------------------------------------------------------- + /** + * Removes a mapping from the map. + *

+ * This implementation calls removeEntry() and destroyEntry(). + * It also handles changes to modCount and size. + * Subclasses could override to fully control removals from the map. + * + * @param entry the entry to remove + * @param hashIndex the index into the data structure + * @param previous the previous entry in the chain + */ + protected void removeMapping(HashEntry entry, int hashIndex, HashEntry previous) { + modCount++; + removeEntry(entry, hashIndex, previous); + size--; + destroyEntry(entry); + } + + /** + * Removes an entry from the chain stored in a particular index. + *

+ * This implementation removes the entry from the data storage table. + * The size is not updated. + * Subclasses could override to handle changes to the map. + * + * @param entry the entry to remove + * @param hashIndex the index into the data structure + * @param previous the previous entry in the chain + */ + protected void removeEntry(HashEntry entry, int hashIndex, HashEntry previous) { + if (previous == null) { + data[hashIndex] = entry.next; + } else { + previous.next = entry.next; + } + } + + /** + * Kills an entry ready for the garbage collector. + *

+ * This implementation prepares the HashEntry for garbage collection. + * Subclasses can override this to implement caching (override clear as well). + * + * @param entry the entry to destroy + */ + protected void destroyEntry(HashEntry entry) { + entry.next = null; + entry.key = null; + entry.value = null; + } + + //----------------------------------------------------------------------- + /** + * Checks the capacity of the map and enlarges it if necessary. + *

+ * This implementation uses the threshold to check if the map needs enlarging + */ + protected void checkCapacity() { + if (size >= threshold) { + int newCapacity = data.length * 2; + if (newCapacity <= MAXIMUM_CAPACITY) { + ensureCapacity(newCapacity); + } + } + } + + /** + * Changes the size of the data structure to the capacity proposed. + * + * @param newCapacity the new capacity of the array (a power of two, less or equal to max) + */ + protected void ensureCapacity(int newCapacity) { + int oldCapacity = data.length; + if (newCapacity <= oldCapacity) { + return; + } + if (size == 0) { + threshold = calculateThreshold(newCapacity, loadFactor); + data = new HashEntry[newCapacity]; + } else { + HashEntry oldEntries[] = data; + HashEntry newEntries[] = new HashEntry[newCapacity]; + + modCount++; + for (int i = oldCapacity - 1; i >= 0; i--) { + HashEntry entry = oldEntries[i]; + if (entry != null) { + oldEntries[i] = null; // gc + do { + HashEntry next = entry.next; + int index = hashIndex(entry.hashCode, newCapacity); + entry.next = newEntries[index]; + newEntries[index] = entry; + entry = next; + } while (entry != null); + } + } + threshold = calculateThreshold(newCapacity, loadFactor); + data = newEntries; + } + } + + /** + * Calculates the new capacity of the map. + * This implementation normalizes the capacity to a power of two. + * + * @param proposedCapacity the proposed capacity + * @return the normalized new capacity + */ + protected int calculateNewCapacity(int proposedCapacity) { + int newCapacity = 1; + if (proposedCapacity > MAXIMUM_CAPACITY) { + newCapacity = MAXIMUM_CAPACITY; + } else { + while (newCapacity < proposedCapacity) { + newCapacity <<= 1; // multiply by two + } + if (newCapacity > MAXIMUM_CAPACITY) { + newCapacity = MAXIMUM_CAPACITY; + } + } + return newCapacity; + } + + /** + * Calculates the new threshold of the map, where it will be resized. + * This implementation uses the load factor. + * + * @param newCapacity the new capacity + * @param factor the load factor + * @return the new resize threshold + */ + protected int calculateThreshold(int newCapacity, float factor) { + return (int) (newCapacity * factor); + } + + //----------------------------------------------------------------------- + /** + * Gets the next field from a HashEntry. + * Used in subclasses that have no visibility of the field. + * + * @param entry the entry to query, must not be null + * @return the next field of the entry + * @throws NullPointerException if the entry is null + * @since Commons Collections 3.1 + */ + protected HashEntry entryNext(HashEntry entry) { + return entry.next; + } + + /** + * Gets the hashCode field from a HashEntry. + * Used in subclasses that have no visibility of the field. + * + * @param entry the entry to query, must not be null + * @return the hashCode field of the entry + * @throws NullPointerException if the entry is null + * @since Commons Collections 3.1 + */ + protected int entryHashCode(HashEntry entry) { + return entry.hashCode; + } + + /** + * Gets the key field from a HashEntry. + * Used in subclasses that have no visibility of the field. + * + * @param entry the entry to query, must not be null + * @return the key field of the entry + * @throws NullPointerException if the entry is null + * @since Commons Collections 3.1 + */ + protected Object entryKey(HashEntry entry) { + return entry.key; + } + + /** + * Gets the value field from a HashEntry. + * Used in subclasses that have no visibility of the field. + * + * @param entry the entry to query, must not be null + * @return the value field of the entry + * @throws NullPointerException if the entry is null + * @since Commons Collections 3.1 + */ + protected Object entryValue(HashEntry entry) { + return entry.value; + } + + //----------------------------------------------------------------------- + /** + * Gets an iterator over the map. + * Changes made to the iterator affect this map. + *

+ * A MapIterator returns the keys in the map. It also provides convenient + * methods to get the key and value, and set the value. + * It avoids the need to create an entrySet/keySet/values object. + * It also avoids creating the Map.Entry object. + * + * @return the map iterator + */ + public MapIterator mapIterator() { + if (size == 0) { + return EmptyMapIterator.INSTANCE; + } + return new HashMapIterator(this); + } + + /** + * MapIterator implementation. + */ + protected static class HashMapIterator extends HashIterator implements MapIterator { + + protected HashMapIterator(AbstractHashedMap parent) { + super(parent); + } + + public Object next() { + return super.nextEntry().getKey(); + } + + public Object getKey() { + HashEntry current = currentEntry(); + if (current == null) { + throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID); + } + return current.getKey(); + } + + public Object getValue() { + HashEntry current = currentEntry(); + if (current == null) { + throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID); + } + return current.getValue(); + } + + public Object setValue(Object value) { + HashEntry current = currentEntry(); + if (current == null) { + throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID); + } + return current.setValue(value); + } + } + + //----------------------------------------------------------------------- + /** + * Gets the entrySet view of the map. + * Changes made to the view affect this map. + * To simply iterate through the entries, use {@link #mapIterator()}. + * + * @return the entrySet view + */ + public Set entrySet() { + if (entrySet == null) { + entrySet = new EntrySet(this); + } + return entrySet; + } + + /** + * Creates an entry set iterator. + * Subclasses can override this to return iterators with different properties. + * + * @return the entrySet iterator + */ + protected Iterator createEntrySetIterator() { + if (size() == 0) { + return EmptyIterator.INSTANCE; + } + return new EntrySetIterator(this); + } + + /** + * EntrySet implementation. + */ + protected static class EntrySet extends AbstractSet { + /** The parent map */ + protected final AbstractHashedMap parent; + + protected EntrySet(AbstractHashedMap parent) { + super(); + this.parent = parent; + } + + public int size() { + return parent.size(); + } + + public void clear() { + parent.clear(); + } + + public boolean contains(Object entry) { + if (entry instanceof Map.Entry) { + Map.Entry e = (Map.Entry) entry; + Entry match = parent.getEntry(e.getKey()); + return (match != null && match.equals(e)); + } + return false; + } + + public boolean remove(Object obj) { + if (obj instanceof Map.Entry == false) { + return false; + } + if (contains(obj) == false) { + return false; + } + Map.Entry entry = (Map.Entry) obj; + Object key = entry.getKey(); + parent.remove(key); + return true; + } + + public Iterator iterator() { + return parent.createEntrySetIterator(); + } + } + + /** + * EntrySet iterator. + */ + protected static class EntrySetIterator extends HashIterator { + + protected EntrySetIterator(AbstractHashedMap parent) { + super(parent); + } + + public Object next() { + return super.nextEntry(); + } + } + + //----------------------------------------------------------------------- + /** + * Gets the keySet view of the map. + * Changes made to the view affect this map. + * To simply iterate through the keys, use {@link #mapIterator()}. + * + * @return the keySet view + */ + public Set keySet() { + if (keySet == null) { + keySet = new KeySet(this); + } + return keySet; + } + + /** + * Creates a key set iterator. + * Subclasses can override this to return iterators with different properties. + * + * @return the keySet iterator + */ + protected Iterator createKeySetIterator() { + if (size() == 0) { + return EmptyIterator.INSTANCE; + } + return new KeySetIterator(this); + } + + /** + * KeySet implementation. + */ + protected static class KeySet extends AbstractSet { + /** The parent map */ + protected final AbstractHashedMap parent; + + protected KeySet(AbstractHashedMap parent) { + super(); + this.parent = parent; + } + + public int size() { + return parent.size(); + } + + public void clear() { + parent.clear(); + } + + public boolean contains(Object key) { + return parent.containsKey(key); + } + + public boolean remove(Object key) { + boolean result = parent.containsKey(key); + parent.remove(key); + return result; + } + + public Iterator iterator() { + return parent.createKeySetIterator(); + } + } + + /** + * KeySet iterator. + */ + protected static class KeySetIterator extends EntrySetIterator { + + protected KeySetIterator(AbstractHashedMap parent) { + super(parent); + } + + public Object next() { + return super.nextEntry().getKey(); + } + } + + //----------------------------------------------------------------------- + /** + * Gets the values view of the map. + * Changes made to the view affect this map. + * To simply iterate through the values, use {@link #mapIterator()}. + * + * @return the values view + */ + public Collection values() { + if (values == null) { + values = new Values(this); + } + return values; + } + + /** + * Creates a values iterator. + * Subclasses can override this to return iterators with different properties. + * + * @return the values iterator + */ + protected Iterator createValuesIterator() { + if (size() == 0) { + return EmptyIterator.INSTANCE; + } + return new ValuesIterator(this); + } + + /** + * Values implementation. + */ + protected static class Values extends AbstractCollection { + /** The parent map */ + protected final AbstractHashedMap parent; + + protected Values(AbstractHashedMap parent) { + super(); + this.parent = parent; + } + + public int size() { + return parent.size(); + } + + public void clear() { + parent.clear(); + } + + public boolean contains(Object value) { + return parent.containsValue(value); + } + + public Iterator iterator() { + return parent.createValuesIterator(); + } + } + + /** + * Values iterator. + */ + protected static class ValuesIterator extends HashIterator { + + protected ValuesIterator(AbstractHashedMap parent) { + super(parent); + } + + public Object next() { + return super.nextEntry().getValue(); + } + } + + //----------------------------------------------------------------------- + /** + * HashEntry used to store the data. + *

+ * If you subclass AbstractHashedMap but not HashEntry + * then you will not be able to access the protected fields. + * The entryXxx() methods on AbstractHashedMap exist + * to provide the necessary access. + */ + protected static class HashEntry implements Map.Entry, KeyValue { + /** The next entry in the hash chain */ + protected HashEntry next; + /** The hash code of the key */ + protected int hashCode; + /** The key */ + protected Object key; + /** The value */ + protected Object value; + + protected HashEntry(HashEntry next, int hashCode, Object key, Object value) { + super(); + this.next = next; + this.hashCode = hashCode; + this.key = key; + this.value = value; + } + + public Object getKey() { + return (key == NULL ? null : key); + } + + public Object getValue() { + return value; + } + + public Object setValue(Object value) { + Object old = this.value; + this.value = value; + return old; + } + + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof Map.Entry == false) { + return false; + } + Map.Entry other = (Map.Entry) obj; + return + (getKey() == null ? other.getKey() == null : getKey().equals(other.getKey())) && + (getValue() == null ? other.getValue() == null : getValue().equals(other.getValue())); + } + + public int hashCode() { + return (getKey() == null ? 0 : getKey().hashCode()) ^ + (getValue() == null ? 0 : getValue().hashCode()); + } + + public String toString() { + return new StringBuffer().append(getKey()).append('=').append(getValue()).toString(); + } + } + + /** + * Base Iterator + */ + protected static abstract class HashIterator implements Iterator { + + /** The parent map */ + protected final AbstractHashedMap parent; + /** The current index into the array of buckets */ + protected int hashIndex; + /** The last returned entry */ + protected HashEntry last; + /** The next entry */ + protected HashEntry next; + /** The modification count expected */ + protected int expectedModCount; + + protected HashIterator(AbstractHashedMap parent) { + super(); + this.parent = parent; + HashEntry[] data = parent.data; + int i = data.length; + HashEntry next = null; + while (i > 0 && next == null) { + next = data[--i]; + } + this.next = next; + this.hashIndex = i; + this.expectedModCount = parent.modCount; + } + + public boolean hasNext() { + return (next != null); + } + + protected HashEntry nextEntry() { + if (parent.modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + HashEntry newCurrent = next; + if (newCurrent == null) { + throw new NoSuchElementException(AbstractHashedMap.NO_NEXT_ENTRY); + } + HashEntry[] data = parent.data; + int i = hashIndex; + HashEntry n = newCurrent.next; + while (n == null && i > 0) { + n = data[--i]; + } + next = n; + hashIndex = i; + last = newCurrent; + return newCurrent; + } + + protected HashEntry currentEntry() { + return last; + } + + public void remove() { + if (last == null) { + throw new IllegalStateException(AbstractHashedMap.REMOVE_INVALID); + } + if (parent.modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + parent.remove(last.getKey()); + last = null; + expectedModCount = parent.modCount; + } + + public String toString() { + if (last != null) { + return "Iterator[" + last.getKey() + "=" + last.getValue() + "]"; + } else { + return "Iterator[]"; + } + } + } + + //----------------------------------------------------------------------- + /** + * Writes the map data to the stream. This method must be overridden if a + * subclass must be setup before put() is used. + *

+ * Serialization is not one of the JDK's nicest topics. Normal serialization will + * initialise the superclass before the subclass. Sometimes however, this isn't + * what you want, as in this case the put() method on read can be + * affected by subclass state. + *

+ * The solution adopted here is to serialize the state data of this class in + * this protected method. This method must be called by the + * writeObject() of the first serializable subclass. + *

+ * Subclasses may override if they have a specific field that must be present + * on read before this implementation will work. Generally, the read determines + * what must be serialized here, if anything. + * + * @param out the output stream + */ + protected void doWriteObject(ObjectOutputStream out) throws IOException { + out.writeFloat(loadFactor); + out.writeInt(data.length); + out.writeInt(size); + for (MapIterator it = mapIterator(); it.hasNext();) { + out.writeObject(it.next()); + out.writeObject(it.getValue()); + } + } + + /** + * Reads the map data from the stream. This method must be overridden if a + * subclass must be setup before put() is used. + *

+ * Serialization is not one of the JDK's nicest topics. Normal serialization will + * initialise the superclass before the subclass. Sometimes however, this isn't + * what you want, as in this case the put() method on read can be + * affected by subclass state. + *

+ * The solution adopted here is to deserialize the state data of this class in + * this protected method. This method must be called by the + * readObject() of the first serializable subclass. + *

+ * Subclasses may override if the subclass has a specific field that must be present + * before put() or calculateThreshold() will work correctly. + * + * @param in the input stream + */ + protected void doReadObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + loadFactor = in.readFloat(); + int capacity = in.readInt(); + int size = in.readInt(); + init(); + threshold = calculateThreshold(capacity, loadFactor); + data = new HashEntry[capacity]; + for (int i = 0; i < size; i++) { + Object key = in.readObject(); + Object value = in.readObject(); + put(key, value); + } + } + + //----------------------------------------------------------------------- + /** + * Clones the map without cloning the keys or values. + *

+ * To implement clone(), a subclass must implement the + * Cloneable interface and make this method public. + * + * @return a shallow clone + */ + protected Object clone() { + try { + AbstractHashedMap cloned = (AbstractHashedMap) super.clone(); + cloned.data = new HashEntry[data.length]; + cloned.entrySet = null; + cloned.keySet = null; + cloned.values = null; + cloned.modCount = 0; + cloned.size = 0; + cloned.init(); + cloned.putAll(this); + return cloned; + + } catch (CloneNotSupportedException ex) { + return null; // should never happen + } + } + + /** + * Compares this map with another. + * + * @param obj the object to compare to + * @return true if equal + */ + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof Map == false) { + return false; + } + Map map = (Map) obj; + if (map.size() != size()) { + return false; + } + MapIterator it = mapIterator(); + try { + while (it.hasNext()) { + Object key = it.next(); + Object value = it.getValue(); + if (value == null) { + if (map.get(key) != null || map.containsKey(key) == false) { + return false; + } + } else { + if (value.equals(map.get(key)) == false) { + return false; + } + } + } + } catch (ClassCastException ignored) { + return false; + } catch (NullPointerException ignored) { + return false; + } + return true; + } + + /** + * Gets the standard Map hashCode. + * + * @return the hash code defined in the Map interface + */ + public int hashCode() { + int total = 0; + Iterator it = createEntrySetIterator(); + while (it.hasNext()) { + total += it.next().hashCode(); + } + return total; + } + + /** + * Gets the map as a String. + * + * @return a string version of the map + */ + public String toString() { + if (size() == 0) { + return "{}"; + } + StringBuffer buf = new StringBuffer(32 * size()); + buf.append('{'); + + MapIterator it = mapIterator(); + boolean hasNext = it.hasNext(); + while (hasNext) { + Object key = it.next(); + Object value = it.getValue(); + buf.append(key == this ? "(this Map)" : key) + .append('=') + .append(value == this ? "(this Map)" : value); + + hasNext = it.hasNext(); + if (hasNext) { + buf.append(',').append(' '); + } + } + + buf.append('}'); + return buf.toString(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/map/AbstractReferenceMap.java b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/map/AbstractReferenceMap.java new file mode 100644 index 00000000..f11224a2 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/map/AbstractReferenceMap.java @@ -0,0 +1,981 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.collections.map; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.ref.Reference; +import java.lang.ref.ReferenceQueue; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +import org.apache.commons.collections.MapIterator; +import org.apache.commons.collections.keyvalue.DefaultMapEntry; + +/** + * An abstract implementation of a hash-based map that allows the entries to + * be removed by the garbage collector. + *

+ * This class implements all the features necessary for a subclass reference + * hash-based map. Key-value entries are stored in instances of the + * ReferenceEntry class which can be overridden and replaced. + * The iterators can similarly be replaced, without the need to replace the KeySet, + * EntrySet and Values view classes. + *

+ * Overridable methods are provided to change the default hashing behaviour, and + * to change how entries are added to and removed from the map. Hopefully, all you + * need for unusual subclasses is here. + *

+ * When you construct an AbstractReferenceMap, you can specify what + * kind of references are used to store the map's keys and values. + * If non-hard references are used, then the garbage collector can remove + * mappings if a key or value becomes unreachable, or if the JVM's memory is + * running low. For information on how the different reference types behave, + * see {@link Reference}. + *

+ * Different types of references can be specified for keys and values. + * The keys can be configured to be weak but the values hard, + * in which case this class will behave like a + * + * WeakHashMap. However, you can also specify hard keys and + * weak values, or any other combination. The default constructor uses + * hard keys and soft values, providing a memory-sensitive cache. + *

+ * This {@link Map} implementation does not allow null elements. + * Attempting to add a null key or value to the map will raise a + * NullPointerException. + *

+ * All the available iterators can be reset back to the start by casting to + * ResettableIterator and calling reset(). + *

+ * This implementation is not synchronized. + * You can use {@link java.util.Collections#synchronizedMap} to + * provide synchronized access to a ReferenceMap. + * + * @see java.lang.ref.Reference + * @since Commons Collections 3.1 (extracted from ReferenceMap in 3.0) + * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ + * + * @author Paul Jack + * @author Stephen Colebourne + */ +public abstract class AbstractReferenceMap extends AbstractHashedMap { + + /** Constant indicating that hard references should be used */ + public static final int HARD = 0; + + /** Constant indicating that soft references should be used */ + public static final int SOFT = 1; + + /** Constant indicating that weak references should be used */ + public static final int WEAK = 2; + + /** + * The reference type for keys. Must be HARD, SOFT, WEAK. + * @serial + */ + protected int keyType; + + /** + * The reference type for values. Must be HARD, SOFT, WEAK. + * @serial + */ + protected int valueType; + + /** + * Should the value be automatically purged when the associated key has been collected? + */ + protected boolean purgeValues; + + /** + * ReferenceQueue used to eliminate stale mappings. + * See purge. + */ + private transient ReferenceQueue queue; + + //----------------------------------------------------------------------- + /** + * Constructor used during deserialization. + */ + protected AbstractReferenceMap() { + super(); + } + + /** + * Constructs a new empty map with the specified reference types, + * load factor and initial capacity. + * + * @param keyType the type of reference to use for keys; + * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK} + * @param valueType the type of reference to use for values; + * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK} + * @param capacity the initial capacity for the map + * @param loadFactor the load factor for the map + * @param purgeValues should the value be automatically purged when the + * key is garbage collected + */ + protected AbstractReferenceMap( + int keyType, int valueType, int capacity, + float loadFactor, boolean purgeValues) { + super(capacity, loadFactor); + verify("keyType", keyType); + verify("valueType", valueType); + this.keyType = keyType; + this.valueType = valueType; + this.purgeValues = purgeValues; + } + + /** + * Initialise this subclass during construction, cloning or deserialization. + */ + protected void init() { + queue = new ReferenceQueue(); + } + + //----------------------------------------------------------------------- + /** + * Checks the type int is a valid value. + * + * @param name the name for error messages + * @param type the type value to check + * @throws IllegalArgumentException if the value if invalid + */ + private static void verify(String name, int type) { + if ((type < HARD) || (type > WEAK)) { + throw new IllegalArgumentException(name + " must be HARD, SOFT, WEAK."); + } + } + + //----------------------------------------------------------------------- + /** + * Gets the size of the map. + * + * @return the size + */ + public int size() { + purgeBeforeRead(); + return super.size(); + } + + /** + * Checks whether the map is currently empty. + * + * @return true if the map is currently size zero + */ + public boolean isEmpty() { + purgeBeforeRead(); + return super.isEmpty(); + } + + /** + * Checks whether the map contains the specified key. + * + * @param key the key to search for + * @return true if the map contains the key + */ + public boolean containsKey(Object key) { + purgeBeforeRead(); + Entry entry = getEntry(key); + if (entry == null) { + return false; + } + return (entry.getValue() != null); + } + + /** + * Checks whether the map contains the specified value. + * + * @param value the value to search for + * @return true if the map contains the value + */ + public boolean containsValue(Object value) { + purgeBeforeRead(); + if (value == null) { + return false; + } + return super.containsValue(value); + } + + /** + * Gets the value mapped to the key specified. + * + * @param key the key + * @return the mapped value, null if no match + */ + public Object get(Object key) { + purgeBeforeRead(); + Entry entry = getEntry(key); + if (entry == null) { + return null; + } + return entry.getValue(); + } + + + /** + * Puts a key-value mapping into this map. + * Neither the key nor the value may be null. + * + * @param key the key to add, must not be null + * @param value the value to add, must not be null + * @return the value previously mapped to this key, null if none + * @throws NullPointerException if either the key or value is null + */ + public Object put(Object key, Object value) { + if (key == null) { + throw new NullPointerException("null keys not allowed"); + } + if (value == null) { + throw new NullPointerException("null values not allowed"); + } + + purgeBeforeWrite(); + return super.put(key, value); + } + + /** + * Removes the specified mapping from this map. + * + * @param key the mapping to remove + * @return the value mapped to the removed key, null if key not in map + */ + public Object remove(Object key) { + if (key == null) { + return null; + } + purgeBeforeWrite(); + return super.remove(key); + } + + /** + * Clears this map. + */ + public void clear() { + super.clear(); + while (queue.poll() != null) {} // drain the queue + } + + //----------------------------------------------------------------------- + /** + * Gets a MapIterator over the reference map. + * The iterator only returns valid key/value pairs. + * + * @return a map iterator + */ + public MapIterator mapIterator() { + return new ReferenceMapIterator(this); + } + + /** + * Returns a set view of this map's entries. + * An iterator returned entry is valid until next() is called again. + * The setValue() method on the toArray entries has no effect. + * + * @return a set view of this map's entries + */ + public Set entrySet() { + if (entrySet == null) { + entrySet = new ReferenceEntrySet(this); + } + return entrySet; + } + + /** + * Returns a set view of this map's keys. + * + * @return a set view of this map's keys + */ + public Set keySet() { + if (keySet == null) { + keySet = new ReferenceKeySet(this); + } + return keySet; + } + + /** + * Returns a collection view of this map's values. + * + * @return a set view of this map's values + */ + public Collection values() { + if (values == null) { + values = new ReferenceValues(this); + } + return values; + } + + //----------------------------------------------------------------------- + /** + * Purges stale mappings from this map before read operations. + *

+ * This implementation calls {@link #purge()} to maintain a consistent state. + */ + protected void purgeBeforeRead() { + purge(); + } + + /** + * Purges stale mappings from this map before write operations. + *

+ * This implementation calls {@link #purge()} to maintain a consistent state. + */ + protected void purgeBeforeWrite() { + purge(); + } + + /** + * Purges stale mappings from this map. + *

+ * Note that this method is not synchronized! Special + * care must be taken if, for instance, you want stale + * mappings to be removed on a periodic basis by some + * background thread. + */ + protected void purge() { + Reference ref = queue.poll(); + while (ref != null) { + purge(ref); + ref = queue.poll(); + } + } + + /** + * Purges the specified reference. + * + * @param ref the reference to purge + */ + protected void purge(Reference ref) { + // The hashCode of the reference is the hashCode of the + // mapping key, even if the reference refers to the + // mapping value... + int hash = ref.hashCode(); + int index = hashIndex(hash, data.length); + HashEntry previous = null; + HashEntry entry = data[index]; + while (entry != null) { + if (((ReferenceEntry) entry).purge(ref)) { + if (previous == null) { + data[index] = entry.next; + } else { + previous.next = entry.next; + } + this.size--; + return; + } + previous = entry; + entry = entry.next; + } + + } + + //----------------------------------------------------------------------- + /** + * Gets the entry mapped to the key specified. + * + * @param key the key + * @return the entry, null if no match + */ + protected HashEntry getEntry(Object key) { + if (key == null) { + return null; + } else { + return super.getEntry(key); + } + } + + /** + * Gets the hash code for a MapEntry. + * Subclasses can override this, for example to use the identityHashCode. + * + * @param key the key to get a hash code for, may be null + * @param value the value to get a hash code for, may be null + * @return the hash code, as per the MapEntry specification + */ + protected int hashEntry(Object key, Object value) { + return (key == null ? 0 : key.hashCode()) ^ + (value == null ? 0 : value.hashCode()); + } + + /** + * Compares two keys, in internal converted form, to see if they are equal. + *

+ * This implementation converts the key from the entry to a real reference + * before comparison. + * + * @param key1 the first key to compare passed in from outside + * @param key2 the second key extracted from the entry via entry.key + * @return true if equal + */ + protected boolean isEqualKey(Object key1, Object key2) { + key2 = (keyType > HARD ? ((Reference) key2).get() : key2); + return (key1 == key2 || key1.equals(key2)); + } + + /** + * Creates a ReferenceEntry instead of a HashEntry. + * + * @param next the next entry in sequence + * @param hashCode the hash code to use + * @param key the key to store + * @param value the value to store + * @return the newly created entry + */ + protected HashEntry createEntry(HashEntry next, int hashCode, Object key, Object value) { + return new ReferenceEntry(this, next, hashCode, key, value); + } + + /** + * Creates an entry set iterator. + * + * @return the entrySet iterator + */ + protected Iterator createEntrySetIterator() { + return new ReferenceEntrySetIterator(this); + } + + /** + * Creates an key set iterator. + * + * @return the keySet iterator + */ + protected Iterator createKeySetIterator() { + return new ReferenceKeySetIterator(this); + } + + /** + * Creates an values iterator. + * + * @return the values iterator + */ + protected Iterator createValuesIterator() { + return new ReferenceValuesIterator(this); + } + + //----------------------------------------------------------------------- + /** + * EntrySet implementation. + */ + static class ReferenceEntrySet extends EntrySet { + + protected ReferenceEntrySet(AbstractHashedMap parent) { + super(parent); + } + + public Object[] toArray() { + return toArray(new Object[0]); + } + + public Object[] toArray(Object[] arr) { + // special implementation to handle disappearing entries + ArrayList list = new ArrayList(); + Iterator iterator = iterator(); + while (iterator.hasNext()) { + Entry e = (Entry) iterator.next(); + list.add(new DefaultMapEntry(e.getKey(), e.getValue())); + } + return list.toArray(arr); + } + } + + //----------------------------------------------------------------------- + /** + * KeySet implementation. + */ + static class ReferenceKeySet extends KeySet { + + protected ReferenceKeySet(AbstractHashedMap parent) { + super(parent); + } + + public Object[] toArray() { + return toArray(new Object[0]); + } + + public Object[] toArray(Object[] arr) { + // special implementation to handle disappearing keys + List list = new ArrayList(parent.size()); + for (Iterator it = iterator(); it.hasNext(); ) { + list.add(it.next()); + } + return list.toArray(arr); + } + } + + //----------------------------------------------------------------------- + /** + * Values implementation. + */ + static class ReferenceValues extends Values { + + protected ReferenceValues(AbstractHashedMap parent) { + super(parent); + } + + public Object[] toArray() { + return toArray(new Object[0]); + } + + public Object[] toArray(Object[] arr) { + // special implementation to handle disappearing values + List list = new ArrayList(parent.size()); + for (Iterator it = iterator(); it.hasNext(); ) { + list.add(it.next()); + } + return list.toArray(arr); + } + } + + //----------------------------------------------------------------------- + /** + * A MapEntry implementation for the map. + *

+ * If getKey() or getValue() returns null, it means + * the mapping is stale and should be removed. + * + * @since Commons Collections 3.1 + */ + protected static class ReferenceEntry extends HashEntry { + /** The parent map */ + protected final AbstractReferenceMap parent; + + /** + * Creates a new entry object for the ReferenceMap. + * + * @param parent the parent map + * @param next the next entry in the hash bucket + * @param hashCode the hash code of the key + * @param key the key + * @param value the value + */ + public ReferenceEntry(AbstractReferenceMap parent, HashEntry next, int hashCode, Object key, Object value) { + super(next, hashCode, null, null); + this.parent = parent; + this.key = toReference(parent.keyType, key, hashCode); + this.value = toReference(parent.valueType, value, hashCode); // the key hashCode is passed in deliberately + } + + /** + * Gets the key from the entry. + * This method dereferences weak and soft keys and thus may return null. + * + * @return the key, which may be null if it was garbage collected + */ + public Object getKey() { + return (parent.keyType > HARD) ? ((Reference) key).get() : key; + } + + /** + * Gets the value from the entry. + * This method dereferences weak and soft value and thus may return null. + * + * @return the value, which may be null if it was garbage collected + */ + public Object getValue() { + return (parent.valueType > HARD) ? ((Reference) value).get() : value; + } + + /** + * Sets the value of the entry. + * + * @param obj the object to store + * @return the previous value + */ + public Object setValue(Object obj) { + Object old = getValue(); + if (parent.valueType > HARD) { + ((Reference)value).clear(); + } + value = toReference(parent.valueType, obj, hashCode); + return old; + } + + /** + * Compares this map entry to another. + *

+ * This implementation uses isEqualKey and + * isEqualValue on the main map for comparison. + * + * @param obj the other map entry to compare to + * @return true if equal, false if not + */ + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof Map.Entry == false) { + return false; + } + + Map.Entry entry = (Map.Entry)obj; + Object entryKey = entry.getKey(); // convert to hard reference + Object entryValue = entry.getValue(); // convert to hard reference + if ((entryKey == null) || (entryValue == null)) { + return false; + } + // compare using map methods, aiding identity subclass + // note that key is direct access and value is via method + return parent.isEqualKey(entryKey, key) && + parent.isEqualValue(entryValue, getValue()); + } + + /** + * Gets the hashcode of the entry using temporary hard references. + *

+ * This implementation uses hashEntry on the main map. + * + * @return the hashcode of the entry + */ + public int hashCode() { + return parent.hashEntry(getKey(), getValue()); + } + + /** + * Constructs a reference of the given type to the given referent. + * The reference is registered with the queue for later purging. + * + * @param type HARD, SOFT or WEAK + * @param referent the object to refer to + * @param hash the hash code of the key of the mapping; + * this number might be different from referent.hashCode() if + * the referent represents a value and not a key + */ + protected Object toReference(int type, Object referent, int hash) { + switch (type) { + case HARD: return referent; + case SOFT: return new SoftRef(hash, referent, parent.queue); + case WEAK: return new WeakRef(hash, referent, parent.queue); + default: throw new Error(); + } + } + + /** + * Purges the specified reference + * @param ref the reference to purge + * @return true or false + */ + boolean purge(Reference ref) { + boolean r = (parent.keyType > HARD) && (key == ref); + r = r || ((parent.valueType > HARD) && (value == ref)); + if (r) { + if (parent.keyType > HARD) { + ((Reference)key).clear(); + } + if (parent.valueType > HARD) { + ((Reference)value).clear(); + } else if (parent.purgeValues) { + value = null; + } + } + return r; + } + + /** + * Gets the next entry in the bucket. + * + * @return the next entry in the bucket + */ + protected ReferenceEntry next() { + return (ReferenceEntry) next; + } + } + + //----------------------------------------------------------------------- + /** + * The EntrySet iterator. + */ + static class ReferenceEntrySetIterator implements Iterator { + /** The parent map */ + final AbstractReferenceMap parent; + + // These fields keep track of where we are in the table. + int index; + ReferenceEntry entry; + ReferenceEntry previous; + + // These Object fields provide hard references to the + // current and next entry; this assures that if hasNext() + // returns true, next() will actually return a valid element. + Object nextKey, nextValue; + Object currentKey, currentValue; + + int expectedModCount; + + public ReferenceEntrySetIterator(AbstractReferenceMap parent) { + super(); + this.parent = parent; + index = (parent.size() != 0 ? parent.data.length : 0); + // have to do this here! size() invocation above + // may have altered the modCount. + expectedModCount = parent.modCount; + } + + public boolean hasNext() { + checkMod(); + while (nextNull()) { + ReferenceEntry e = entry; + int i = index; + while ((e == null) && (i > 0)) { + i--; + e = (ReferenceEntry) parent.data[i]; + } + entry = e; + index = i; + if (e == null) { + currentKey = null; + currentValue = null; + return false; + } + nextKey = e.getKey(); + nextValue = e.getValue(); + if (nextNull()) { + entry = entry.next(); + } + } + return true; + } + + private void checkMod() { + if (parent.modCount != expectedModCount) { + throw new ConcurrentModificationException(); + } + } + + private boolean nextNull() { + return (nextKey == null) || (nextValue == null); + } + + protected ReferenceEntry nextEntry() { + checkMod(); + if (nextNull() && !hasNext()) { + throw new NoSuchElementException(); + } + previous = entry; + entry = entry.next(); + currentKey = nextKey; + currentValue = nextValue; + nextKey = null; + nextValue = null; + return previous; + } + + protected ReferenceEntry currentEntry() { + checkMod(); + return previous; + } + + public Object next() { + return nextEntry(); + } + + public void remove() { + checkMod(); + if (previous == null) { + throw new IllegalStateException(); + } + parent.remove(currentKey); + previous = null; + currentKey = null; + currentValue = null; + expectedModCount = parent.modCount; + } + } + + /** + * The keySet iterator. + */ + static class ReferenceKeySetIterator extends ReferenceEntrySetIterator { + + ReferenceKeySetIterator(AbstractReferenceMap parent) { + super(parent); + } + + public Object next() { + return nextEntry().getKey(); + } + } + + /** + * The values iterator. + */ + static class ReferenceValuesIterator extends ReferenceEntrySetIterator { + + ReferenceValuesIterator(AbstractReferenceMap parent) { + super(parent); + } + + public Object next() { + return nextEntry().getValue(); + } + } + + /** + * The MapIterator implementation. + */ + static class ReferenceMapIterator extends ReferenceEntrySetIterator implements MapIterator { + + protected ReferenceMapIterator(AbstractReferenceMap parent) { + super(parent); + } + + public Object next() { + return nextEntry().getKey(); + } + + public Object getKey() { + HashEntry current = currentEntry(); + if (current == null) { + throw new IllegalStateException(AbstractHashedMap.GETKEY_INVALID); + } + return current.getKey(); + } + + public Object getValue() { + HashEntry current = currentEntry(); + if (current == null) { + throw new IllegalStateException(AbstractHashedMap.GETVALUE_INVALID); + } + return current.getValue(); + } + + public Object setValue(Object value) { + HashEntry current = currentEntry(); + if (current == null) { + throw new IllegalStateException(AbstractHashedMap.SETVALUE_INVALID); + } + return current.setValue(value); + } + } + + //----------------------------------------------------------------------- + // These two classes store the hashCode of the key of + // of the mapping, so that after they're dequeued a quick + // lookup of the bucket in the table can occur. + + /** + * A soft reference holder. + */ + static class SoftRef extends SoftReference { + /** the hashCode of the key (even if the reference points to a value) */ + private int hash; + + public SoftRef(int hash, Object r, ReferenceQueue q) { + super(r, q); + this.hash = hash; + } + + public int hashCode() { + return hash; + } + } + + /** + * A weak reference holder. + */ + static class WeakRef extends WeakReference { + /** the hashCode of the key (even if the reference points to a value) */ + private int hash; + + public WeakRef(int hash, Object r, ReferenceQueue q) { + super(r, q); + this.hash = hash; + } + + public int hashCode() { + return hash; + } + } + + //----------------------------------------------------------------------- + /** + * Replaces the superclass method to store the state of this class. + *

+ * Serialization is not one of the JDK's nicest topics. Normal serialization will + * initialise the superclass before the subclass. Sometimes however, this isn't + * what you want, as in this case the put() method on read can be + * affected by subclass state. + *

+ * The solution adopted here is to serialize the state data of this class in + * this protected method. This method must be called by the + * writeObject() of the first serializable subclass. + *

+ * Subclasses may override if they have a specific field that must be present + * on read before this implementation will work. Generally, the read determines + * what must be serialized here, if anything. + * + * @param out the output stream + */ + protected void doWriteObject(ObjectOutputStream out) throws IOException { + out.writeInt(keyType); + out.writeInt(valueType); + out.writeBoolean(purgeValues); + out.writeFloat(loadFactor); + out.writeInt(data.length); + for (MapIterator it = mapIterator(); it.hasNext();) { + out.writeObject(it.next()); + out.writeObject(it.getValue()); + } + out.writeObject(null); // null terminate map + // do not call super.doWriteObject() as code there doesn't work for reference map + } + + /** + * Replaces the superclassm method to read the state of this class. + *

+ * Serialization is not one of the JDK's nicest topics. Normal serialization will + * initialise the superclass before the subclass. Sometimes however, this isn't + * what you want, as in this case the put() method on read can be + * affected by subclass state. + *

+ * The solution adopted here is to deserialize the state data of this class in + * this protected method. This method must be called by the + * readObject() of the first serializable subclass. + *

+ * Subclasses may override if the subclass has a specific field that must be present + * before put() or calculateThreshold() will work correctly. + * + * @param in the input stream + */ + protected void doReadObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + this.keyType = in.readInt(); + this.valueType = in.readInt(); + this.purgeValues = in.readBoolean(); + this.loadFactor = in.readFloat(); + int capacity = in.readInt(); + init(); + data = new HashEntry[capacity]; + while (true) { + Object key = in.readObject(); + if (key == null) { + break; + } + Object value = in.readObject(); + put(key, value); + } + threshold = calculateThreshold(data.length, loadFactor); + // do not call super.doReadObject() as code there doesn't work for reference map + } + +} diff --git a/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/map/ReferenceMap.java b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/map/ReferenceMap.java new file mode 100644 index 00000000..fa057997 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/apache/commons/collections/map/ReferenceMap.java @@ -0,0 +1,169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.collections.map; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; + +/** + * A Map implementation that allows mappings to be + * removed by the garbage collector. + *

+ * When you construct a ReferenceMap, you can specify what kind + * of references are used to store the map's keys and values. + * If non-hard references are used, then the garbage collector can remove + * mappings if a key or value becomes unreachable, or if the JVM's memory is + * running low. For information on how the different reference types behave, + * see {@link java.lang.ref.Reference Reference}. + *

+ * Different types of references can be specified for keys and values. + * The keys can be configured to be weak but the values hard, + * in which case this class will behave like a + * + * WeakHashMap. However, you can also specify hard keys and + * weak values, or any other combination. The default constructor uses + * hard keys and soft values, providing a memory-sensitive cache. + *

+ * This map is similar to + * {@link org.apache.commons.collections.map.ReferenceIdentityMap ReferenceIdentityMap}. + * It differs in that keys and values in this class are compared using equals(). + *

+ * This {@link java.util.Map Map} implementation does not allow null elements. + * Attempting to add a null key or value to the map will raise a NullPointerException. + *

+ * This implementation is not synchronized. + * You can use {@link java.util.Collections#synchronizedMap} to + * provide synchronized access to a ReferenceMap. + * Remember that synchronization will not stop the garbage collecter removing entries. + *

+ * All the available iterators can be reset back to the start by casting to + * ResettableIterator and calling reset(). + *

+ * Note that ReferenceMap is not synchronized and is not thread-safe. + * If you wish to use this map from multiple threads concurrently, you must use + * appropriate synchronization. The simplest approach is to wrap this map + * using {@link java.util.Collections#synchronizedMap}. This class may throw + * exceptions when accessed by concurrent threads without synchronization. + *

+ * NOTE: As from Commons Collections 3.1 this map extends AbstractReferenceMap + * (previously it extended AbstractMap). As a result, the implementation is now + * extensible and provides a MapIterator. + * + * @see java.lang.ref.Reference + * + * @since Commons Collections 3.0 (previously in main package v2.1) + * @version $Revision: 646777 $ $Date: 2008-04-10 13:33:15 +0100 (Thu, 10 Apr 2008) $ + * + * @author Paul Jack + * @author Stephen Colebourne + */ +public class ReferenceMap extends AbstractReferenceMap implements Serializable { + + /** Serialization version */ + private static final long serialVersionUID = 1555089888138299607L; + + /** + * Constructs a new ReferenceMap that will + * use hard references to keys and soft references to values. + */ + public ReferenceMap() { + super(HARD, SOFT, DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, false); + } + + /** + * Constructs a new ReferenceMap that will + * use the specified types of references. + * + * @param keyType the type of reference to use for keys; + * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK} + * @param valueType the type of reference to use for values; + * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK} + */ + public ReferenceMap(int keyType, int valueType) { + super(keyType, valueType, DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, false); + } + + /** + * Constructs a new ReferenceMap that will + * use the specified types of references. + * + * @param keyType the type of reference to use for keys; + * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK} + * @param valueType the type of reference to use for values; + * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK} + * @param purgeValues should the value be automatically purged when the + * key is garbage collected + */ + public ReferenceMap(int keyType, int valueType, boolean purgeValues) { + super(keyType, valueType, DEFAULT_CAPACITY, DEFAULT_LOAD_FACTOR, purgeValues); + } + + /** + * Constructs a new ReferenceMap with the + * specified reference types, load factor and initial + * capacity. + * + * @param keyType the type of reference to use for keys; + * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK} + * @param valueType the type of reference to use for values; + * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK} + * @param capacity the initial capacity for the map + * @param loadFactor the load factor for the map + */ + public ReferenceMap(int keyType, int valueType, int capacity, float loadFactor) { + super(keyType, valueType, capacity, loadFactor, false); + } + + /** + * Constructs a new ReferenceMap with the + * specified reference types, load factor and initial + * capacity. + * + * @param keyType the type of reference to use for keys; + * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK} + * @param valueType the type of reference to use for values; + * must be {@link #HARD}, {@link #SOFT}, {@link #WEAK} + * @param capacity the initial capacity for the map + * @param loadFactor the load factor for the map + * @param purgeValues should the value be automatically purged when the + * key is garbage collected + */ + public ReferenceMap(int keyType, int valueType, int capacity, + float loadFactor, boolean purgeValues) { + super(keyType, valueType, capacity, loadFactor, purgeValues); + } + + //----------------------------------------------------------------------- + /** + * Write the map out using a custom routine. + */ + private void writeObject(ObjectOutputStream out) throws IOException { + out.defaultWriteObject(); + doWriteObject(out); + } + + /** + * Read the map in using a custom routine. + */ + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + in.defaultReadObject(); + doReadObject(in); + } + +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java new file mode 100644 index 00000000..83bc39d2 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1ApplicationSpecificParser.java @@ -0,0 +1,10 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +public interface ASN1ApplicationSpecificParser + extends DEREncodable +{ + DEREncodable readObject() + throws IOException; +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Choice.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Choice.java new file mode 100644 index 00000000..603131d1 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Choice.java @@ -0,0 +1,14 @@ +package org.bouncycastle.asn1; + +/** + * Marker interface for CHOICE objects - if you implement this in a role your + * own object any attempt to tag the object implicitly will convert the tag to + * an explicit one as the encoding rules require. + *

+ * If you use this interface your class should also implement the getInstance + * pattern which takes a tag object and the tagging mode used. + */ +public interface ASN1Choice +{ + // marker interface +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Encodable.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Encodable.java new file mode 100644 index 00000000..77573a28 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Encodable.java @@ -0,0 +1,102 @@ +package org.bouncycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * Base class for objects which can be written directly to ASN.1 output streams. + */ +public abstract class ASN1Encodable + implements DEREncodable +{ + public static final String DER = "DER"; + public static final String BER = "BER"; + + /** + * Return the default BER or DER encoding for this object. + * + * @return BER/DER byte encoded object. + * @throws IOException on encoding error. + */ + public byte[] getEncoded() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ASN1OutputStream aOut = new ASN1OutputStream(bOut); + + aOut.writeObject(this); + + return bOut.toByteArray(); + } + + /** + * Return either the default for "BER" or a DER encoding if "DER" is specified. + * + * @param encoding name of encoding to use. + * @return byte encoded object. + * @throws IOException on encoding error. + */ + public byte[] getEncoded( + String encoding) + throws IOException + { + if (encoding.equals(DER)) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(bOut); + + dOut.writeObject(this); + + return bOut.toByteArray(); + } + + return this.getEncoded(); + } + + /** + * Return the DER encoding of the object, null if the DER encoding can not be made. + * + * @return a DER byte array, null otherwise. + */ + public byte[] getDEREncoded() + { + try + { + return this.getEncoded(DER); + } + catch (IOException e) + { + return null; + } + } + + public int hashCode() + { + return this.toASN1Object().hashCode(); + } + + public boolean equals( + Object o) + { + if (this == o) + { + return true; + } + + if (!(o instanceof DEREncodable)) + { + return false; + } + + DEREncodable other = (DEREncodable)o; + + return this.toASN1Object().equals(other.getDERObject()); + } + + public DERObject getDERObject() + { + return this.toASN1Object(); + } + + public abstract DERObject toASN1Object(); +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1EncodableVector.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1EncodableVector.java new file mode 100644 index 00000000..7e9bf529 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1EncodableVector.java @@ -0,0 +1,15 @@ +package org.bouncycastle.asn1; + +/** + * the parent class for this will eventually disappear. Use this one! + */ +public class ASN1EncodableVector + extends DEREncodableVector +{ + // migrating from DEREncodeableVector + @SuppressWarnings("deprecation") + public ASN1EncodableVector() + { + + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Generator.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Generator.java new file mode 100644 index 00000000..50cb7054 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Generator.java @@ -0,0 +1,15 @@ +package org.bouncycastle.asn1; + +import java.io.OutputStream; + +public abstract class ASN1Generator +{ + protected OutputStream _out; + + public ASN1Generator(OutputStream out) + { + _out = out; + } + + public abstract OutputStream getRawOutputStream(); +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1InputStream.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1InputStream.java new file mode 100644 index 00000000..45c34a3e --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1InputStream.java @@ -0,0 +1,387 @@ +package org.bouncycastle.asn1; + +import java.io.ByteArrayInputStream; +import java.io.EOFException; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.bouncycastle.util.io.Streams; + +/** + * a general purpose ASN.1 decoder - note: this class differs from the + * others in that it returns null after it has read the last object in + * the stream. If an ASN.1 NULL is encountered a DER/BER Null object is + * returned. + */ +public class ASN1InputStream + extends FilterInputStream + implements DERTags +{ + private final int limit; + private final boolean lazyEvaluate; + + public ASN1InputStream( + InputStream is) + { + this(is, Integer.MAX_VALUE); + } + + /** + * Create an ASN1InputStream based on the input byte array. The length of DER objects in + * the stream is automatically limited to the length of the input array. + * + * @param input array containing ASN.1 encoded data. + */ + public ASN1InputStream( + byte[] input) + { + this(new ByteArrayInputStream(input), input.length); + } + + /** + * Create an ASN1InputStream based on the input byte array. The length of DER objects in + * the stream is automatically limited to the length of the input array. + * + * @param input array containing ASN.1 encoded data. + * @param lazyEvaluate true if parsing inside constructed objects can be delayed. + */ + public ASN1InputStream( + byte[] input, + boolean lazyEvaluate) + { + this(new ByteArrayInputStream(input), input.length, lazyEvaluate); + } + + /** + * Create an ASN1InputStream where no DER object will be longer than limit. + * + * @param input stream containing ASN.1 encoded data. + * @param limit maximum size of a DER encoded object. + */ + public ASN1InputStream( + InputStream input, + int limit) + { + this(input, limit, false); + } + + /** + * Create an ASN1InputStream where no DER object will be longer than limit, and constructed + * objects such as sequences will be parsed lazily. + * + * @param input stream containing ASN.1 encoded data. + * @param limit maximum size of a DER encoded object. + * @param lazyEvaluate true if parsing inside constructed objects can be delayed. + */ + public ASN1InputStream( + InputStream input, + int limit, + boolean lazyEvaluate) + { + super(input); + this.limit = limit; + this.lazyEvaluate = lazyEvaluate; + } + + protected int readLength() + throws IOException + { + return readLength(this, limit); + } + + protected void readFully( + byte[] bytes) + throws IOException + { + if (Streams.readFully(this, bytes) != bytes.length) + { + throw new EOFException("EOF encountered in middle of object"); + } + } + + /** + * build an object given its tag and the number of bytes to construct it from. + */ + protected DERObject buildObject( + int tag, + int tagNo, + int length) + throws IOException + { + boolean isConstructed = (tag & CONSTRUCTED) != 0; + + DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(this, length); + + if ((tag & APPLICATION) != 0) + { + return new DERApplicationSpecific(isConstructed, tagNo, defIn.toByteArray()); + } + + if ((tag & TAGGED) != 0) + { + return new BERTaggedObjectParser(tag, tagNo, defIn).getDERObject(); + } + + if (isConstructed) + { + // TODO There are other tags that may be constructed (e.g. BIT_STRING) + switch (tagNo) + { + case OCTET_STRING: + // + // yes, people actually do this... + // + return new BERConstructedOctetString(buildDEREncodableVector(defIn).v); + case SEQUENCE: + if (lazyEvaluate) + { + assert(false); + //return new LazyDERSequence(defIn.toByteArray()); + } + else + { + return DERFactory.createSequence(buildDEREncodableVector(defIn)); + } + case SET: + return DERFactory.createSet(buildDEREncodableVector(defIn), false); + case EXTERNAL: + return new DERExternal(buildDEREncodableVector(defIn)); + default: + return new DERUnknownTag(true, tagNo, defIn.toByteArray()); + } + } + + return createPrimitiveDERObject(tagNo, defIn.toByteArray()); + } + + ASN1EncodableVector buildEncodableVector() + throws IOException + { + ASN1EncodableVector v = new ASN1EncodableVector(); + DERObject o; + + while ((o = readObject()) != null) + { + v.add(o); + } + + return v; + } + + ASN1EncodableVector buildDEREncodableVector( + DefiniteLengthInputStream dIn) throws IOException + { + return new ASN1InputStream(dIn).buildEncodableVector(); + } + + public DERObject readObject() + throws IOException + { + int tag = read(); + if (tag <= 0) + { + if (tag == 0) + { + throw new IOException("unexpected end-of-contents marker"); + } + + return null; + } + + // + // calculate tag number + // + int tagNo = readTagNumber(this, tag); + + boolean isConstructed = (tag & CONSTRUCTED) != 0; + + // + // calculate length + // + int length = readLength(); + + if (length < 0) // indefinite length method + { + if (!isConstructed) + { + throw new IOException("indefinite length primitive encoding encountered"); + } + + IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(this); + + if ((tag & APPLICATION) != 0) + { + ASN1StreamParser sp = new ASN1StreamParser(indIn, limit); + + return new BERApplicationSpecificParser(tagNo, sp).getDERObject(); + } + if ((tag & TAGGED) != 0) + { + return new BERTaggedObjectParser(tag, tagNo, indIn).getDERObject(); + } + + ASN1StreamParser sp = new ASN1StreamParser(indIn, limit); + + // TODO There are other tags that may be constructed (e.g. BIT_STRING) + switch (tagNo) + { + case OCTET_STRING: + return new BEROctetStringParser(sp).getDERObject(); + case SEQUENCE: + return new BERSequenceParser(sp).getDERObject(); + case SET: + return new BERSetParser(sp).getDERObject(); + case EXTERNAL: + return new DERExternalParser(sp).getDERObject(); + default: + throw new IOException("unknown BER object encountered"); + } + } + else + { + return buildObject(tag, tagNo, length); + } + } + + static int readTagNumber(InputStream s, int tag) + throws IOException + { + int tagNo = tag & 0x1f; + + // + // with tagged object tag number is bottom 5 bits, or stored at the start of the content + // + if (tagNo == 0x1f) + { + tagNo = 0; + + int b = s.read(); + + // X.690-0207 8.1.2.4.2 + // "c) bits 7 to 1 of the first subsequent octet shall not all be zero." + if ((b & 0x7f) == 0) // Note: -1 will pass + { + throw new IOException("corrupted stream - invalid high tag number found"); + } + + while ((b >= 0) && ((b & 0x80) != 0)) + { + tagNo |= (b & 0x7f); + tagNo <<= 7; + b = s.read(); + } + + if (b < 0) + { + throw new EOFException("EOF found inside tag value."); + } + + tagNo |= (b & 0x7f); + } + + return tagNo; + } + + static int readLength(InputStream s, int limit) + throws IOException + { + int length = s.read(); + if (length < 0) + { + throw new EOFException("EOF found when length expected"); + } + + if (length == 0x80) + { + return -1; // indefinite-length encoding + } + + if (length > 127) + { + int size = length & 0x7f; + + if (size > 4) + { + throw new IOException("DER length more than 4 bytes: " + size); + } + + length = 0; + for (int i = 0; i < size; i++) + { + int next = s.read(); + + if (next < 0) + { + throw new EOFException("EOF found reading length"); + } + + length = (length << 8) + next; + } + + if (length < 0) + { + throw new IOException("corrupted stream - negative length found"); + } + + if (length >= limit) // after all we must have read at least 1 byte + { + throw new IOException("corrupted stream - out of bounds length found"); + } + } + + return length; + } + + static DERObject createPrimitiveDERObject( + int tagNo, + byte[] bytes) + { + switch (tagNo) + { + case BIT_STRING: + { + int padBits = bytes[0]; + byte[] data = new byte[bytes.length - 1]; + System.arraycopy(bytes, 1, data, 0, bytes.length - 1); + return new DERBitString(data, padBits); + } + case BMP_STRING: + return new DERBMPString(bytes); + case BOOLEAN: + return new DERBoolean(bytes); + case ENUMERATED: + return new DEREnumerated(bytes); + case GENERALIZED_TIME: + return new DERGeneralizedTime(bytes); + case GENERAL_STRING: + return new DERGeneralString(bytes); + case IA5_STRING: + return new DERIA5String(bytes); + case INTEGER: + return new DERInteger(bytes); + case NULL: + return DERNull.INSTANCE; // actual content is ignored (enforce 0 length?) + case NUMERIC_STRING: + return new DERNumericString(bytes); + case OBJECT_IDENTIFIER: + return new DERObjectIdentifier(bytes); + case OCTET_STRING: + return new DEROctetString(bytes); + case PRINTABLE_STRING: + return new DERPrintableString(bytes); + case T61_STRING: + return new DERT61String(bytes); + case UNIVERSAL_STRING: + return new DERUniversalString(bytes); + case UTC_TIME: + return new DERUTCTime(bytes); + case UTF8_STRING: + return new DERUTF8String(bytes); + case VISIBLE_STRING: + return new DERVisibleString(bytes); + default: + return new DERUnknownTag(false, tagNo, bytes); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Null.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Null.java new file mode 100644 index 00000000..b9b849d2 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Null.java @@ -0,0 +1,38 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +/** + * A NULL object. + */ +public abstract class ASN1Null + extends ASN1Object +{ + public ASN1Null() + { + } + + public int hashCode() + { + return -1; + } + + boolean asn1Equals( + DERObject o) + { + if (!(o instanceof ASN1Null)) + { + return false; + } + + return true; + } + + abstract void encode(DEROutputStream out) + throws IOException; + + public String toString() + { + return "NULL"; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Object.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Object.java new file mode 100644 index 00000000..7a0b1137 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Object.java @@ -0,0 +1,38 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +public abstract class ASN1Object + extends DERObject +{ + /** + * Create a base ASN.1 object from a byte stream. + * + * @param data the byte stream to parse. + * @return the base ASN.1 object represented by the byte stream. + * @exception IOException if there is a problem parsing the data. + */ + public static ASN1Object fromByteArray(byte[] data) + throws IOException + { + ASN1InputStream aIn = new ASN1InputStream(data); + + return (ASN1Object)aIn.readObject(); + } + + public final boolean equals(Object o) + { + if (this == o) + { + return true; + } + + return (o instanceof DEREncodable) && asn1Equals(((DEREncodable)o).getDERObject()); + } + + public abstract int hashCode(); + + abstract void encode(DEROutputStream out) throws IOException; + + abstract boolean asn1Equals(DERObject o); +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1ObjectParser.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1ObjectParser.java new file mode 100644 index 00000000..ff09a454 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1ObjectParser.java @@ -0,0 +1,19 @@ +package org.bouncycastle.asn1; + +import java.io.InputStream; + +/** + * @deprecated will be removed + */ +public class ASN1ObjectParser +{ + ASN1StreamParser _aIn; + + protected ASN1ObjectParser( + int baseTag, + int tagNumber, + InputStream contentStream) + { + _aIn = new ASN1StreamParser(contentStream); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1OctetString.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1OctetString.java new file mode 100644 index 00000000..d235458e --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1OctetString.java @@ -0,0 +1,136 @@ +package org.bouncycastle.asn1; + +import org.bouncycastle.util.encoders.Hex; +import org.bouncycastle.util.Arrays; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Enumeration; +import java.util.Vector; + +public abstract class ASN1OctetString + extends ASN1Object + implements ASN1OctetStringParser +{ + byte[] string; + + /** + * return an Octet String from a tagged object. + * + * @param obj the tagged object holding the object we want. + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static ASN1OctetString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + /** + * return an Octet String from the given object. + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + @SuppressWarnings("unchecked") + public static ASN1OctetString getInstance( + Object obj) + { + if (obj == null || obj instanceof ASN1OctetString) + { + return (ASN1OctetString)obj; + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + if (obj instanceof ASN1Sequence) + { + Vector v = new Vector(); + Enumeration e = ((ASN1Sequence)obj).getObjects(); + + while (e.hasMoreElements()) + { + v.addElement(e.nextElement()); + } + + return new BERConstructedOctetString(v); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * @param string the octets making up the octet string. + */ + public ASN1OctetString( + byte[] string) + { + if (string == null) + { + throw new NullPointerException("string cannot be null"); + } + this.string = string; + } + + public ASN1OctetString( + DEREncodable obj) + { + try + { + this.string = obj.getDERObject().getEncoded(ASN1Encodable.DER); + } + catch (IOException e) + { + throw new IllegalArgumentException("Error processing object : " + e.toString()); + } + } + + public InputStream getOctetStream() + { + return new ByteArrayInputStream(string); + } + + public ASN1OctetStringParser parser() + { + return this; + } + + public byte[] getOctets() + { + return string; + } + + public int hashCode() + { + return Arrays.hashCode(this.getOctets()); + } + + boolean asn1Equals( + DERObject o) + { + if (!(o instanceof ASN1OctetString)) + { + return false; + } + + ASN1OctetString other = (ASN1OctetString)o; + + return Arrays.areEqual(string, other.string); + } + + abstract void encode(DEROutputStream out) + throws IOException; + + public String toString() + { + return "#"+new String(Hex.encode(string)); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1OctetStringParser.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1OctetStringParser.java new file mode 100644 index 00000000..641020c1 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1OctetStringParser.java @@ -0,0 +1,9 @@ +package org.bouncycastle.asn1; + +import java.io.InputStream; + +public interface ASN1OctetStringParser + extends DEREncodable +{ + public InputStream getOctetStream(); +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1OutputStream.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1OutputStream.java new file mode 100644 index 00000000..5897d09f --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1OutputStream.java @@ -0,0 +1,36 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; +import java.io.OutputStream; + +public class ASN1OutputStream + extends DEROutputStream +{ + public ASN1OutputStream( + OutputStream os) + { + super(os); + } + + public void writeObject( + Object obj) + throws IOException + { + if (obj == null) + { + writeNull(); + } + else if (obj instanceof DERObject) + { + ((DERObject)obj).encode(this); + } + else if (obj instanceof DEREncodable) + { + ((DEREncodable)obj).getDERObject().encode(this); + } + else + { + throw new IOException("object not ASN1Encodable"); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1ParsingException.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1ParsingException.java new file mode 100644 index 00000000..2f4dc818 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1ParsingException.java @@ -0,0 +1,24 @@ +package org.bouncycastle.asn1; + +@SuppressWarnings("serial") +public class ASN1ParsingException + extends IllegalStateException +{ + private Throwable cause; + + ASN1ParsingException(String message) + { + super(message); + } + + ASN1ParsingException(String message, Throwable cause) + { + super(message); + this.cause = cause; + } + + public Throwable getCause() + { + return cause; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Sequence.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Sequence.java new file mode 100644 index 00000000..da80adec --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Sequence.java @@ -0,0 +1,218 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.Vector; + +@SuppressWarnings("unchecked") +public abstract class ASN1Sequence + extends ASN1Object +{ + private Vector seq = new Vector(); + + /** + * return an ASN1Sequence from the given object. + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static ASN1Sequence getInstance( + Object obj) + { + if (obj == null || obj instanceof ASN1Sequence) + { + return (ASN1Sequence)obj; + } + + throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); + } + + /** + * Return an ASN1 sequence from a tagged object. There is a special + * case here, if an object appears to have been explicitly tagged on + * reading but we were expecting it to be implicitly tagged in the + * normal course of events it indicates that we lost the surrounding + * sequence - so we need to add it back (this will happen if the tagged + * object is a sequence that contains other sequences). If you are + * dealing with implicitly tagged sequences you really should + * be using this method. + * + * @param obj the tagged object. + * @param explicit true if the object is meant to be explicitly tagged, + * false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static ASN1Sequence getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + if (explicit) + { + if (!obj.isExplicit()) + { + throw new IllegalArgumentException("object implicit - explicit expected."); + } + + return (ASN1Sequence)obj.getObject(); + } + else + { + // + // constructed object which appears to be explicitly tagged + // when it should be implicit means we have to add the + // surrounding sequence. + // + if (obj.isExplicit()) + { + if (obj instanceof BERTaggedObject) + { + return new BERSequence(obj.getObject()); + } + else + { + return new DERSequence(obj.getObject()); + } + } + else + { + if (obj.getObject() instanceof ASN1Sequence) + { + return (ASN1Sequence)obj.getObject(); + } + } + } + + throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); + } + + public Enumeration getObjects() + { + return seq.elements(); + } + + public ASN1SequenceParser parser() + { + final ASN1Sequence outer = this; + + return new ASN1SequenceParser() + { + private final int max = size(); + + private int index; + + public DEREncodable readObject() throws IOException + { + if (index == max) + { + return null; + } + + DEREncodable obj = getObjectAt(index++); + if (obj instanceof ASN1Sequence) + { + return ((ASN1Sequence)obj).parser(); + } + if (obj instanceof ASN1Set) + { + return ((ASN1Set)obj).parser(); + } + + return obj; + } + + public DERObject getDERObject() + { + return outer; + } + }; + } + + /** + * return the object at the sequence position indicated by index. + * + * @param index the sequence number (starting at zero) of the object + * @return the object at the sequence position indicated by index. + */ + public DEREncodable getObjectAt( + int index) + { + return (DEREncodable)seq.elementAt(index); + } + + /** + * return the number of objects in this sequence. + * + * @return the number of objects in this sequence. + */ + public int size() + { + return seq.size(); + } + + public int hashCode() + { + Enumeration e = this.getObjects(); + int hashCode = size(); + + while (e.hasMoreElements()) + { + Object o = e.nextElement(); + hashCode *= 17; + if (o != null) + { + hashCode ^= o.hashCode(); + } + } + + return hashCode; + } + + boolean asn1Equals( + DERObject o) + { + if (!(o instanceof ASN1Sequence)) + { + return false; + } + + ASN1Sequence other = (ASN1Sequence)o; + + if (this.size() != other.size()) + { + return false; + } + + Enumeration s1 = this.getObjects(); + Enumeration s2 = other.getObjects(); + + while (s1.hasMoreElements()) + { + DERObject o1 = ((DEREncodable)s1.nextElement()).getDERObject(); + DERObject o2 = ((DEREncodable)s2.nextElement()).getDERObject(); + + if (o1 == o2 || (o1 != null && o1.equals(o2))) + { + continue; + } + + return false; + } + + return true; + } + + protected void addObject( + DEREncodable obj) + { + seq.addElement(obj); + } + + abstract void encode(DEROutputStream out) + throws IOException; + + public String toString() + { + return seq.toString(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1SequenceParser.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1SequenceParser.java new file mode 100644 index 00000000..ceda6bdd --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1SequenceParser.java @@ -0,0 +1,10 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +public interface ASN1SequenceParser + extends DEREncodable +{ + DEREncodable readObject() + throws IOException; +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Set.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Set.java new file mode 100644 index 00000000..2eaafe0e --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1Set.java @@ -0,0 +1,344 @@ +package org.bouncycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Vector; + +@SuppressWarnings("unchecked") +abstract public class ASN1Set + extends ASN1Object +{ + protected Vector set = new Vector(); + + /** + * return an ASN1Set from the given object. + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static ASN1Set getInstance( + Object obj) + { + if (obj == null || obj instanceof ASN1Set) + { + return (ASN1Set)obj; + } + + throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); + } + + /** + * Return an ASN1 set from a tagged object. There is a special + * case here, if an object appears to have been explicitly tagged on + * reading but we were expecting it to be implicitly tagged in the + * normal course of events it indicates that we lost the surrounding + * set - so we need to add it back (this will happen if the tagged + * object is a sequence that contains other sequences). If you are + * dealing with implicitly tagged sets you really should + * be using this method. + * + * @param obj the tagged object. + * @param explicit true if the object is meant to be explicitly tagged + * false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static ASN1Set getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + if (explicit) + { + if (!obj.isExplicit()) + { + throw new IllegalArgumentException("object implicit - explicit expected."); + } + + return (ASN1Set)obj.getObject(); + } + else + { + // + // constructed object which appears to be explicitly tagged + // and it's really implicit means we have to add the + // surrounding sequence. + // + if (obj.isExplicit()) + { + ASN1Set set = new DERSet(obj.getObject()); + + return set; + } + else + { + if (obj.getObject() instanceof ASN1Set) + { + return (ASN1Set)obj.getObject(); + } + + // + // in this case the parser returns a sequence, convert it + // into a set. + // + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (obj.getObject() instanceof ASN1Sequence) + { + ASN1Sequence s = (ASN1Sequence)obj.getObject(); + Enumeration e = s.getObjects(); + + while (e.hasMoreElements()) + { + v.add((DEREncodable)e.nextElement()); + } + + return new DERSet(v, false); + } + } + } + + throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); + } + + public ASN1Set() + { + } + + public Enumeration getObjects() + { + return set.elements(); + } + + /** + * return the object at the set position indicated by index. + * + * @param index the set number (starting at zero) of the object + * @return the object at the set position indicated by index. + */ + public DEREncodable getObjectAt( + int index) + { + return (DEREncodable)set.elementAt(index); + } + + /** + * return the number of objects in this set. + * + * @return the number of objects in this set. + */ + public int size() + { + return set.size(); + } + + public ASN1SetParser parser() + { + final ASN1Set outer = this; + + return new ASN1SetParser() + { + private final int max = size(); + + private int index; + + public DEREncodable readObject() throws IOException + { + if (index == max) + { + return null; + } + + DEREncodable obj = getObjectAt(index++); + if (obj instanceof ASN1Sequence) + { + return ((ASN1Sequence)obj).parser(); + } + if (obj instanceof ASN1Set) + { + return ((ASN1Set)obj).parser(); + } + + return obj; + } + + public DERObject getDERObject() + { + return outer; + } + }; + } + + public int hashCode() + { + Enumeration e = this.getObjects(); + int hashCode = size(); + + while (e.hasMoreElements()) + { + Object o = e.nextElement(); + hashCode *= 17; + if (o != null) + { + hashCode ^= o.hashCode(); + } + } + + return hashCode; + } + + boolean asn1Equals( + DERObject o) + { + if (!(o instanceof ASN1Set)) + { + return false; + } + + ASN1Set other = (ASN1Set)o; + + if (this.size() != other.size()) + { + return false; + } + + Enumeration s1 = this.getObjects(); + Enumeration s2 = other.getObjects(); + + while (s1.hasMoreElements()) + { + DERObject o1 = ((DEREncodable)s1.nextElement()).getDERObject(); + DERObject o2 = ((DEREncodable)s2.nextElement()).getDERObject(); + + if (o1 == o2 || (o1 != null && o1.equals(o2))) + { + continue; + } + + return false; + } + + return true; + } + + /** + * return true if a <= b (arrays are assumed padded with zeros). + */ + private boolean lessThanOrEqual( + byte[] a, + byte[] b) + { + if (a.length <= b.length) + { + for (int i = 0; i != a.length; i++) + { + int l = a[i] & 0xff; + int r = b[i] & 0xff; + + if (r > l) + { + return true; + } + else if (l > r) + { + return false; + } + } + + return true; + } + else + { + for (int i = 0; i != b.length; i++) + { + int l = a[i] & 0xff; + int r = b[i] & 0xff; + + if (r > l) + { + return true; + } + else if (l > r) + { + return false; + } + } + + return false; + } + } + + private byte[] getEncoded( + DEREncodable obj) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ASN1OutputStream aOut = new ASN1OutputStream(bOut); + + try + { + aOut.writeObject(obj); + } + catch (IOException e) + { + throw new IllegalArgumentException("cannot encode object added to SET"); + } + + return bOut.toByteArray(); + } + + protected void sort() + { + if (set.size() > 1) + { + boolean swapped = true; + int lastSwap = set.size() - 1; + + while (swapped) + { + int index = 0; + int swapIndex = 0; + byte[] a = getEncoded((DEREncodable)set.elementAt(0)); + + swapped = false; + + while (index != lastSwap) + { + byte[] b = getEncoded((DEREncodable)set.elementAt(index + 1)); + + if (lessThanOrEqual(a, b)) + { + a = b; + } + else + { + Object o = set.elementAt(index); + + set.setElementAt(set.elementAt(index + 1), index); + set.setElementAt(o, index + 1); + + swapped = true; + swapIndex = index; + } + + index++; + } + + lastSwap = swapIndex; + } + } + } + + protected void addObject( + DEREncodable obj) + { + set.addElement(obj); + } + + abstract void encode(DEROutputStream out) + throws IOException; + + public String toString() + { + return set.toString(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1SetParser.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1SetParser.java new file mode 100644 index 00000000..b09a1704 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1SetParser.java @@ -0,0 +1,10 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +public interface ASN1SetParser + extends DEREncodable +{ + public DEREncodable readObject() + throws IOException; +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1StreamParser.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1StreamParser.java new file mode 100644 index 00000000..43fcad76 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1StreamParser.java @@ -0,0 +1,174 @@ +package org.bouncycastle.asn1; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; + +public class ASN1StreamParser +{ + private final InputStream _in; + private final int _limit; + + private static int findLimit(InputStream in) + { + if (in instanceof DefiniteLengthInputStream) + { + return ((DefiniteLengthInputStream)in).getRemaining(); + } + + return Integer.MAX_VALUE; + } + + public ASN1StreamParser( + InputStream in) + { + this(in, findLimit(in)); + } + + public ASN1StreamParser( + InputStream in, + int limit) + { + this._in = in; + this._limit = limit; + } + + public ASN1StreamParser( + byte[] encoding) + { + this(new ByteArrayInputStream(encoding), encoding.length); + } + + public DEREncodable readObject() + throws IOException + { + int tag = _in.read(); + if (tag == -1) + { + return null; + } + + // + // turn of looking for "00" while we resolve the tag + // + set00Check(false); + + // + // calculate tag number + // + int tagNo = ASN1InputStream.readTagNumber(_in, tag); + + boolean isConstructed = (tag & DERTags.CONSTRUCTED) != 0; + + // + // calculate length + // + int length = ASN1InputStream.readLength(_in, _limit); + + if (length < 0) // indefinite length method + { + if (!isConstructed) + { + throw new IOException("indefinite length primitive encoding encountered"); + } + + IndefiniteLengthInputStream indIn = new IndefiniteLengthInputStream(_in); + + if ((tag & DERTags.APPLICATION) != 0) + { + ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit); + + return new BERApplicationSpecificParser(tagNo, sp); + } + + if ((tag & DERTags.TAGGED) != 0) + { + return new BERTaggedObjectParser(tag, tagNo, indIn); + } + + ASN1StreamParser sp = new ASN1StreamParser(indIn, _limit); + + // TODO There are other tags that may be constructed (e.g. BIT_STRING) + switch (tagNo) + { + case DERTags.OCTET_STRING: + return new BEROctetStringParser(sp); + case DERTags.SEQUENCE: + return new BERSequenceParser(sp); + case DERTags.SET: + return new BERSetParser(sp); + case DERTags.EXTERNAL:{ + return new DERExternalParser(sp); + } + default: + throw new IOException("unknown BER object encountered: 0x" + Integer.toHexString(tagNo)); + } + } + else + { + DefiniteLengthInputStream defIn = new DefiniteLengthInputStream(_in, length); + + if ((tag & DERTags.APPLICATION) != 0) + { + return new DERApplicationSpecific(isConstructed, tagNo, defIn.toByteArray()); + } + + if ((tag & DERTags.TAGGED) != 0) + { + return new BERTaggedObjectParser(tag, tagNo, defIn); + } + + if (isConstructed) + { + // TODO There are other tags that may be constructed (e.g. BIT_STRING) + switch (tagNo) + { + case DERTags.OCTET_STRING: + // + // yes, people actually do this... + // + return new BEROctetStringParser(new ASN1StreamParser(defIn)); + case DERTags.SEQUENCE: + return new DERSequenceParser(new ASN1StreamParser(defIn)); + case DERTags.SET: + return new DERSetParser(new ASN1StreamParser(defIn)); + case DERTags.EXTERNAL: + return new DERExternalParser(new ASN1StreamParser(defIn)); + default: + // TODO Add DERUnknownTagParser class? + return new DERUnknownTag(true, tagNo, defIn.toByteArray()); + } + } + + // Some primitive encodings can be handled by parsers too... + switch (tagNo) + { + case DERTags.OCTET_STRING: + return new DEROctetStringParser(defIn); + } + + return ASN1InputStream.createPrimitiveDERObject(tagNo, defIn.toByteArray()); + } + } + + private void set00Check(boolean enabled) + { + if (_in instanceof IndefiniteLengthInputStream) + { + ((IndefiniteLengthInputStream)_in).setEofOn00(enabled); + } + } + + ASN1EncodableVector readVector() throws IOException + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + DEREncodable obj; + while ((obj = readObject()) != null) + { + v.add(obj.getDERObject()); + } + + return v; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1TaggedObject.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1TaggedObject.java new file mode 100644 index 00000000..1e5d4e88 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1TaggedObject.java @@ -0,0 +1,210 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +/** + * ASN.1 TaggedObject - in ASN.1 notation this is any object preceded by + * a [n] where n is some number - these are assumed to follow the construction + * rules (as with sequences). + */ +public abstract class ASN1TaggedObject + extends ASN1Object + implements ASN1TaggedObjectParser +{ + int tagNo; + boolean empty = false; + boolean explicit = true; + DEREncodable obj = null; + + static public ASN1TaggedObject getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + if (explicit) + { + return (ASN1TaggedObject)obj.getObject(); + } + + throw new IllegalArgumentException("implicitly tagged tagged object"); + } + + static public ASN1TaggedObject getInstance( + Object obj) + { + if (obj == null || obj instanceof ASN1TaggedObject) + { + return (ASN1TaggedObject)obj; + } + + throw new IllegalArgumentException("unknown object in getInstance: " + obj.getClass().getName()); + } + + /** + * Create a tagged object in the explicit style. + * + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + public ASN1TaggedObject( + int tagNo, + DEREncodable obj) + { + this.explicit = true; + this.tagNo = tagNo; + this.obj = obj; + } + + /** + * Create a tagged object with the style given by the value of explicit. + *

+ * If the object implements ASN1Choice the tag style will always be changed + * to explicit in accordance with the ASN.1 encoding rules. + *

+ * @param explicit true if the object is explicitly tagged. + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + public ASN1TaggedObject( + boolean explicit, + int tagNo, + DEREncodable obj) + { + if (obj instanceof ASN1Choice) + { + this.explicit = true; + } + else + { + this.explicit = explicit; + } + + this.tagNo = tagNo; + this.obj = obj; + } + + boolean asn1Equals( + DERObject o) + { + if (!(o instanceof ASN1TaggedObject)) + { + return false; + } + + ASN1TaggedObject other = (ASN1TaggedObject)o; + + if (tagNo != other.tagNo || empty != other.empty || explicit != other.explicit) + { + return false; + } + + if(obj == null) + { + if (other.obj != null) + { + return false; + } + } + else + { + if (!(obj.getDERObject().equals(other.obj.getDERObject()))) + { + return false; + } + } + + return true; + } + + public int hashCode() + { + int code = tagNo; + + // TODO: actually this is wrong - the problem is that a re-encoded + // object may end up with a different hashCode due to implicit + // tagging. As implicit tagging is ambiguous if a sequence is involved + // it seems the only correct method for both equals and hashCode is to + // compare the encodings... + if (obj != null) + { + code ^= obj.hashCode(); + } + + return code; + } + + public int getTagNo() + { + return tagNo; + } + + /** + * return whether or not the object may be explicitly tagged. + *

+ * Note: if the object has been read from an input stream, the only + * time you can be sure if isExplicit is returning the true state of + * affairs is if it returns false. An implicitly tagged object may appear + * to be explicitly tagged, so you need to understand the context under + * which the reading was done as well, see getObject below. + */ + public boolean isExplicit() + { + return explicit; + } + + public boolean isEmpty() + { + return empty; + } + + /** + * return whatever was following the tag. + *

+ * Note: tagged objects are generally context dependent if you're + * trying to extract a tagged object you should be going via the + * appropriate getInstance method. + */ + public DERObject getObject() + { + if (obj != null) + { + return obj.getDERObject(); + } + + return null; + } + + /** + * Return the object held in this tagged object as a parser assuming it has + * the type of the passed in tag. If the object doesn't have a parser + * associated with it, the base object is returned. + */ + public DEREncodable getObjectParser( + int tag, + boolean isExplicit) + { + switch (tag) + { + case DERTags.SET: + return ASN1Set.getInstance(this, isExplicit).parser(); + case DERTags.SEQUENCE: + return ASN1Sequence.getInstance(this, isExplicit).parser(); + case DERTags.OCTET_STRING: + return ASN1OctetString.getInstance(this, isExplicit).parser(); + } + + if (isExplicit) + { + return getObject(); + } + + throw new RuntimeException("implicit tagging not implemented for tag: " + tag); + } + + abstract void encode(DEROutputStream out) + throws IOException; + + public String toString() + { + return "[" + tagNo + "]" + obj; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1TaggedObjectParser.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1TaggedObjectParser.java new file mode 100644 index 00000000..5574bf83 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ASN1TaggedObjectParser.java @@ -0,0 +1,12 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +public interface ASN1TaggedObjectParser + extends DEREncodable +{ + public int getTagNo(); + + public DEREncodable getObjectParser(int tag, boolean isExplicit) + throws IOException; +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERApplicationSpecific.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERApplicationSpecific.java new file mode 100644 index 00000000..8bc8a4eb --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERApplicationSpecific.java @@ -0,0 +1,10 @@ +package org.bouncycastle.asn1; + +public class BERApplicationSpecific + extends DERApplicationSpecific +{ + public BERApplicationSpecific(int tagNo, ASN1EncodableVector vec) + { + super(tagNo, vec); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERApplicationSpecificParser.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERApplicationSpecificParser.java new file mode 100644 index 00000000..0c46ba34 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERApplicationSpecificParser.java @@ -0,0 +1,34 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +public class BERApplicationSpecificParser + implements ASN1ApplicationSpecificParser +{ + private final int tag; + private final ASN1StreamParser parser; + + BERApplicationSpecificParser(int tag, ASN1StreamParser parser) + { + this.tag = tag; + this.parser = parser; + } + + public DEREncodable readObject() + throws IOException + { + return parser.readObject(); + } + + public DERObject getDERObject() + { + try + { + return new BERApplicationSpecific(tag, parser.readVector()); + } + catch (IOException e) + { + throw new ASN1ParsingException(e.getMessage(), e); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERConstructedOctetString.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERConstructedOctetString.java new file mode 100644 index 00000000..dfa8ac47 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERConstructedOctetString.java @@ -0,0 +1,145 @@ +package org.bouncycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Vector; + +@SuppressWarnings("unchecked") +public class BERConstructedOctetString + extends DEROctetString +{ + private static final int MAX_LENGTH = 1000; + + /** + * convert a vector of octet strings into a single byte string + */ + static private byte[] toBytes( + Vector octs) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + for (int i = 0; i != octs.size(); i++) + { + try + { + DEROctetString o = (DEROctetString)octs.elementAt(i); + + bOut.write(o.getOctets()); + } + catch (ClassCastException e) + { + throw new IllegalArgumentException(octs.elementAt(i).getClass().getName() + " found in input should only contain DEROctetString"); + } + catch (IOException e) + { + throw new IllegalArgumentException("exception converting octets " + e.toString()); + } + } + + return bOut.toByteArray(); + } + + private Vector octs; + + /** + * @param string the octets making up the octet string. + */ + public BERConstructedOctetString( + byte[] string) + { + super(string); + } + + public BERConstructedOctetString( + Vector octs) + { + super(toBytes(octs)); + + this.octs = octs; + } + + public BERConstructedOctetString( + DERObject obj) + { + super(obj); + } + + public BERConstructedOctetString( + DEREncodable obj) + { + super(obj.getDERObject()); + } + + public byte[] getOctets() + { + return string; + } + + /** + * return the DER octets that make up this string. + */ + public Enumeration getObjects() + { + if (octs == null) + { + return generateOcts().elements(); + } + + return octs.elements(); + } + + private Vector generateOcts() + { + Vector vec = new Vector(); + for (int i = 0; i < string.length; i += MAX_LENGTH) + { + int end; + + if (i + MAX_LENGTH > string.length) + { + end = string.length; + } + else + { + end = i + MAX_LENGTH; + } + + byte[] nStr = new byte[end - i]; + + System.arraycopy(string, i, nStr, 0, nStr.length); + + vec.addElement(new DEROctetString(nStr)); + } + + return vec; + } + + public void encode( + DEROutputStream out) + throws IOException + { + if (out instanceof ASN1OutputStream || out instanceof BEROutputStream) + { + out.write(CONSTRUCTED | OCTET_STRING); + + out.write(0x80); + + // + // write out the octet array + // + Enumeration e = getObjects(); + while (e.hasMoreElements()) + { + out.writeObject(e.nextElement()); + } + + out.write(0x00); + out.write(0x00); + } + else + { + super.encode(out); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERConstructedSequence.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERConstructedSequence.java new file mode 100644 index 00000000..9e1d6a59 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERConstructedSequence.java @@ -0,0 +1,38 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; +import java.util.Enumeration; + +/** + * @deprecated use BERSequence + */ +@SuppressWarnings("unchecked") +public class BERConstructedSequence + extends DERConstructedSequence +{ + /* + */ + void encode( + DEROutputStream out) + throws IOException + { + if (out instanceof ASN1OutputStream || out instanceof BEROutputStream) + { + out.write(SEQUENCE | CONSTRUCTED); + out.write(0x80); + + Enumeration e = getObjects(); + while (e.hasMoreElements()) + { + out.writeObject(e.nextElement()); + } + + out.write(0x00); + out.write(0x00); + } + else + { + super.encode(out); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERFactory.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERFactory.java new file mode 100644 index 00000000..82f101c7 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERFactory.java @@ -0,0 +1,22 @@ +package org.bouncycastle.asn1; + +class BERFactory +{ + static final BERSequence EMPTY_SEQUENCE = new BERSequence(); + static final BERSet EMPTY_SET = new BERSet(); + + static BERSequence createSequence(ASN1EncodableVector v) + { + return v.size() < 1 ? EMPTY_SEQUENCE : new BERSequence(v); + } + + static BERSet createSet(ASN1EncodableVector v) + { + return v.size() < 1 ? EMPTY_SET : new BERSet(v); + } + + static BERSet createSet(ASN1EncodableVector v, boolean needsSorting) + { + return v.size() < 1 ? EMPTY_SET : new BERSet(v, needsSorting); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERGenerator.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERGenerator.java new file mode 100644 index 00000000..6b327419 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERGenerator.java @@ -0,0 +1,100 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class BERGenerator + extends ASN1Generator +{ + private boolean _tagged = false; + private boolean _isExplicit; + private int _tagNo; + + protected BERGenerator( + OutputStream out) + { + super(out); + } + + public BERGenerator( + OutputStream out, + int tagNo, + boolean isExplicit) + { + super(out); + + _tagged = true; + _isExplicit = isExplicit; + _tagNo = tagNo; + } + + public OutputStream getRawOutputStream() + { + return _out; + } + + private void writeHdr( + int tag) + throws IOException + { + _out.write(tag); + _out.write(0x80); + } + + protected void writeBERHeader( + int tag) + throws IOException + { + if (_tagged) + { + int tagNum = _tagNo | DERTags.TAGGED; + + if (_isExplicit) + { + writeHdr(tagNum | DERTags.CONSTRUCTED); + writeHdr(tag); + } + else + { + if ((tag & DERTags.CONSTRUCTED) != 0) + { + writeHdr(tagNum | DERTags.CONSTRUCTED); + } + else + { + writeHdr(tagNum); + } + } + } + else + { + writeHdr(tag); + } + } + + protected void writeBERBody( + InputStream contentStream) + throws IOException + { + int ch; + + while ((ch = contentStream.read()) >= 0) + { + _out.write(ch); + } + } + + protected void writeBEREnd() + throws IOException + { + _out.write(0x00); + _out.write(0x00); + + if (_tagged && _isExplicit) // write extra end for tag header + { + _out.write(0x00); + _out.write(0x00); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERInputStream.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERInputStream.java new file mode 100644 index 00000000..1dfa06d9 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERInputStream.java @@ -0,0 +1,210 @@ +package org.bouncycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; +import java.util.Vector; + +/** + * @deprecated use ASN1InputStream + */ +@SuppressWarnings("unchecked") +public class BERInputStream + extends DERInputStream +{ + private static final DERObject END_OF_STREAM = new DERObject() + { + void encode( + DEROutputStream out) + throws IOException + { + throw new IOException("Eeek!"); + } + public int hashCode() + { + return 0; + } + public boolean equals( + Object o) + { + return o == this; + } + }; + public BERInputStream( + InputStream is) + { + super(is); + } + + /** + * read a string of bytes representing an indefinite length object. + */ + private byte[] readIndefiniteLengthFully() + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + int b, b1; + + b1 = read(); + + while ((b = read()) >= 0) + { + if (b1 == 0 && b == 0) + { + break; + } + + bOut.write(b1); + b1 = b; + } + + return bOut.toByteArray(); + } + + private BERConstructedOctetString buildConstructedOctetString() + throws IOException + { + Vector octs = new Vector(); + + for (;;) + { + DERObject o = readObject(); + + if (o == END_OF_STREAM) + { + break; + } + + octs.addElement(o); + } + + return new BERConstructedOctetString(octs); + } + + public DERObject readObject() + throws IOException + { + int tag = read(); + if (tag == -1) + { + throw new EOFException(); + } + + int length = readLength(); + + if (length < 0) // indefinite length method + { + switch (tag) + { + case NULL: + return null; + case SEQUENCE | CONSTRUCTED: + BERConstructedSequence seq = new BERConstructedSequence(); + + for (;;) + { + DERObject obj = readObject(); + + if (obj == END_OF_STREAM) + { + break; + } + + seq.addObject(obj); + } + return seq; + case OCTET_STRING | CONSTRUCTED: + return buildConstructedOctetString(); + case SET | CONSTRUCTED: + ASN1EncodableVector v = new ASN1EncodableVector(); + + for (;;) + { + DERObject obj = readObject(); + + if (obj == END_OF_STREAM) + { + break; + } + + v.add(obj); + } + return new BERSet(v); + default: + // + // with tagged object tag number is bottom 5 bits + // + if ((tag & TAGGED) != 0) + { + if ((tag & 0x1f) == 0x1f) + { + throw new IOException("unsupported high tag encountered"); + } + + // + // simple type - implicit... return an octet string + // + if ((tag & CONSTRUCTED) == 0) + { + byte[] bytes = readIndefiniteLengthFully(); + + return new BERTaggedObject(false, tag & 0x1f, new DEROctetString(bytes)); + } + + // + // either constructed or explicitly tagged + // + DERObject dObj = readObject(); + + if (dObj == END_OF_STREAM) // empty tag! + { + return new DERTaggedObject(tag & 0x1f); + } + + DERObject next = readObject(); + + // + // explicitly tagged (probably!) - if it isn't we'd have to + // tell from the context + // + if (next == END_OF_STREAM) + { + return new BERTaggedObject(tag & 0x1f, dObj); + } + + // + // another implicit object, we'll create a sequence... + // + seq = new BERConstructedSequence(); + + seq.addObject(dObj); + + do + { + seq.addObject(next); + next = readObject(); + } + while (next != END_OF_STREAM); + + return new BERTaggedObject(false, tag & 0x1f, seq); + } + + throw new IOException("unknown BER object encountered"); + } + } + else + { + if (tag == 0 && length == 0) // end of contents marker. + { + return END_OF_STREAM; + } + + byte[] bytes = new byte[length]; + + readFully(bytes); + + return buildObject(tag, bytes); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERNull.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERNull.java new file mode 100644 index 00000000..92bc10da --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERNull.java @@ -0,0 +1,30 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +/** + * A BER NULL object. + */ +public class BERNull + extends DERNull +{ + public static final BERNull INSTANCE = new BERNull(); + + public BERNull() + { + } + + void encode( + DEROutputStream out) + throws IOException + { + if (out instanceof ASN1OutputStream || out instanceof BEROutputStream) + { + out.write(NULL); + } + else + { + super.encode(out); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BEROctetStringGenerator.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BEROctetStringGenerator.java new file mode 100644 index 00000000..61ef8245 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BEROctetStringGenerator.java @@ -0,0 +1,102 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; +import java.io.OutputStream; + +public class BEROctetStringGenerator + extends BERGenerator +{ + public BEROctetStringGenerator(OutputStream out) + throws IOException + { + super(out); + + writeBERHeader(DERTags.CONSTRUCTED | DERTags.OCTET_STRING); + } + + public BEROctetStringGenerator( + OutputStream out, + int tagNo, + boolean isExplicit) + throws IOException + { + super(out, tagNo, isExplicit); + + writeBERHeader(DERTags.CONSTRUCTED | DERTags.OCTET_STRING); + } + + public OutputStream getOctetOutputStream() + { + return getOctetOutputStream(new byte[1000]); // limit for CER encoding. + } + + public OutputStream getOctetOutputStream( + byte[] buf) + { + return new BufferedBEROctetStream(buf); + } + + private class BufferedBEROctetStream + extends OutputStream + { + private byte[] _buf; + private int _off; + private DEROutputStream _derOut; + + BufferedBEROctetStream( + byte[] buf) + { + _buf = buf; + _off = 0; + _derOut = new DEROutputStream(_out); + } + + public void write( + int b) + throws IOException + { + _buf[_off++] = (byte)b; + + if (_off == _buf.length) + { + DEROctetString.encode(_derOut, _buf); + _off = 0; + } + } + + public void write(byte[] b, int off, int len) throws IOException + { + while (len > 0) + { + int numToCopy = Math.min(len, _buf.length - _off); + System.arraycopy(b, off, _buf, _off, numToCopy); + + _off += numToCopy; + if (_off < _buf.length) + { + break; + } + + DEROctetString.encode(_derOut, _buf); + _off = 0; + + off += numToCopy; + len -= numToCopy; + } + } + + public void close() + throws IOException + { + if (_off != 0) + { + byte[] bytes = new byte[_off]; + System.arraycopy(_buf, 0, bytes, 0, _off); + + DEROctetString.encode(_derOut, bytes); + } + + writeBEREnd(); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BEROctetStringParser.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BEROctetStringParser.java new file mode 100644 index 00000000..bc937ad9 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BEROctetStringParser.java @@ -0,0 +1,44 @@ +package org.bouncycastle.asn1; + +import org.bouncycastle.util.io.Streams; + +import java.io.InputStream; +import java.io.IOException; + +public class BEROctetStringParser + implements ASN1OctetStringParser +{ + private ASN1StreamParser _parser; + + BEROctetStringParser( + ASN1StreamParser parser) + { + _parser = parser; + } + + /** + * @deprecated will be removed + */ + protected BEROctetStringParser( + ASN1ObjectParser parser) + { + _parser = parser._aIn; + } + + public InputStream getOctetStream() + { + return new ConstructedOctetStream(_parser); + } + + public DERObject getDERObject() + { + try + { + return new BERConstructedOctetString(Streams.readAll(getOctetStream())); + } + catch (IOException e) + { + throw new ASN1ParsingException("IOException converting stream to byte array: " + e.getMessage(), e); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BEROutputStream.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BEROutputStream.java new file mode 100644 index 00000000..c2e8da45 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BEROutputStream.java @@ -0,0 +1,36 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; +import java.io.OutputStream; + +public class BEROutputStream + extends DEROutputStream +{ + public BEROutputStream( + OutputStream os) + { + super(os); + } + + public void writeObject( + Object obj) + throws IOException + { + if (obj == null) + { + writeNull(); + } + else if (obj instanceof DERObject) + { + ((DERObject)obj).encode(this); + } + else if (obj instanceof DEREncodable) + { + ((DEREncodable)obj).getDERObject().encode(this); + } + else + { + throw new IOException("object not BEREncodable"); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERSequence.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERSequence.java new file mode 100644 index 00000000..5054fb52 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERSequence.java @@ -0,0 +1,60 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; +import java.util.Enumeration; + +@SuppressWarnings("unchecked") +public class BERSequence + extends DERSequence +{ + /** + * create an empty sequence + */ + public BERSequence() + { + } + + /** + * create a sequence containing one object + */ + public BERSequence( + DEREncodable obj) + { + super(obj); + } + + /** + * create a sequence containing a vector of objects. + */ + public BERSequence( + DEREncodableVector v) + { + super(v); + } + + /* + */ + void encode( + DEROutputStream out) + throws IOException + { + if (out instanceof ASN1OutputStream || out instanceof BEROutputStream) + { + out.write(SEQUENCE | CONSTRUCTED); + out.write(0x80); + + Enumeration e = getObjects(); + while (e.hasMoreElements()) + { + out.writeObject(e.nextElement()); + } + + out.write(0x00); + out.write(0x00); + } + else + { + super.encode(out); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERSequenceGenerator.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERSequenceGenerator.java new file mode 100644 index 00000000..bc634205 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERSequenceGenerator.java @@ -0,0 +1,41 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; +import java.io.OutputStream; + +public class BERSequenceGenerator + extends BERGenerator +{ + public BERSequenceGenerator( + OutputStream out) + throws IOException + { + super(out); + + writeBERHeader(DERTags.CONSTRUCTED | DERTags.SEQUENCE); + } + + public BERSequenceGenerator( + OutputStream out, + int tagNo, + boolean isExplicit) + throws IOException + { + super(out, tagNo, isExplicit); + + writeBERHeader(DERTags.CONSTRUCTED | DERTags.SEQUENCE); + } + + public void addObject( + DEREncodable object) + throws IOException + { + object.getDERObject().encode(new BEROutputStream(_out)); + } + + public void close() + throws IOException + { + writeBEREnd(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERSequenceParser.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERSequenceParser.java new file mode 100644 index 00000000..cd0ca279 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERSequenceParser.java @@ -0,0 +1,32 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +public class BERSequenceParser + implements ASN1SequenceParser +{ + private ASN1StreamParser _parser; + + BERSequenceParser(ASN1StreamParser parser) + { + this._parser = parser; + } + + public DEREncodable readObject() + throws IOException + { + return _parser.readObject(); + } + + public DERObject getDERObject() + { + try + { + return new BERSequence(_parser.readVector()); + } + catch (IOException e) + { + throw new IllegalStateException(e.getMessage()); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERSet.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERSet.java new file mode 100644 index 00000000..9f332029 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERSet.java @@ -0,0 +1,70 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; +import java.util.Enumeration; + +@SuppressWarnings("unchecked") +public class BERSet + extends DERSet +{ + /** + * create an empty sequence + */ + public BERSet() + { + } + + /** + * create a set containing one object + */ + public BERSet( + DEREncodable obj) + { + super(obj); + } + + /** + * @param v - a vector of objects making up the set. + */ + public BERSet( + DEREncodableVector v) + { + super(v, false); + } + + /** + * @param v - a vector of objects making up the set. + */ + BERSet( + DEREncodableVector v, + boolean needsSorting) + { + super(v, needsSorting); + } + + /* + */ + void encode( + DEROutputStream out) + throws IOException + { + if (out instanceof ASN1OutputStream || out instanceof BEROutputStream) + { + out.write(SET | CONSTRUCTED); + out.write(0x80); + + Enumeration e = getObjects(); + while (e.hasMoreElements()) + { + out.writeObject(e.nextElement()); + } + + out.write(0x00); + out.write(0x00); + } + else + { + super.encode(out); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERSetParser.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERSetParser.java new file mode 100644 index 00000000..ac280d3f --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERSetParser.java @@ -0,0 +1,32 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +public class BERSetParser + implements ASN1SetParser +{ + private ASN1StreamParser _parser; + + BERSetParser(ASN1StreamParser parser) + { + this._parser = parser; + } + + public DEREncodable readObject() + throws IOException + { + return _parser.readObject(); + } + + public DERObject getDERObject() + { + try + { + return new BERSet(_parser.readVector(), false); + } + catch (IOException e) + { + throw new ASN1ParsingException(e.getMessage(), e); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERTaggedObject.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERTaggedObject.java new file mode 100644 index 00000000..990c1432 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERTaggedObject.java @@ -0,0 +1,108 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; +import java.util.Enumeration; + +/** + * BER TaggedObject - in ASN.1 notation this is any object preceded by + * a [n] where n is some number - these are assumed to follow the construction + * rules (as with sequences). + */ +@SuppressWarnings("unchecked") +public class BERTaggedObject + extends DERTaggedObject +{ + /** + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + public BERTaggedObject( + int tagNo, + DEREncodable obj) + { + super(tagNo, obj); + } + + /** + * @param explicit true if an explicitly tagged object. + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + public BERTaggedObject( + boolean explicit, + int tagNo, + DEREncodable obj) + { + super(explicit, tagNo, obj); + } + + /** + * create an implicitly tagged object that contains a zero + * length sequence. + */ + public BERTaggedObject( + int tagNo) + { + super(false, tagNo, new BERSequence()); + } + + void encode( + DEROutputStream out) + throws IOException + { + if (out instanceof ASN1OutputStream || out instanceof BEROutputStream) + { + out.writeTag(CONSTRUCTED | TAGGED, tagNo); + out.write(0x80); + + if (!empty) + { + if (!explicit) + { + Enumeration e; + if (obj instanceof ASN1OctetString) + { + if (obj instanceof BERConstructedOctetString) + { + e = ((BERConstructedOctetString)obj).getObjects(); + } + else + { + ASN1OctetString octs = (ASN1OctetString)obj; + BERConstructedOctetString berO = new BERConstructedOctetString(octs.getOctets()); + e = berO.getObjects(); + } + } + else if (obj instanceof ASN1Sequence) + { + e = ((ASN1Sequence)obj).getObjects(); + } + else if (obj instanceof ASN1Set) + { + e = ((ASN1Set)obj).getObjects(); + } + else + { + throw new RuntimeException("not implemented: " + obj.getClass().getName()); + } + + while (e.hasMoreElements()) + { + out.writeObject(e.nextElement()); + } + } + else + { + out.writeObject(obj); + } + } + + out.write(0x00); + out.write(0x00); + } + else + { + super.encode(out); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERTaggedObjectParser.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERTaggedObjectParser.java new file mode 100644 index 00000000..ce7318d2 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/BERTaggedObjectParser.java @@ -0,0 +1,123 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; +import java.io.InputStream; + +public class BERTaggedObjectParser + implements ASN1TaggedObjectParser +{ + private int _baseTag; + private int _tagNumber; + private InputStream _contentStream; + + private boolean _indefiniteLength; + + protected BERTaggedObjectParser( + int baseTag, + int tagNumber, + InputStream contentStream) + { + _baseTag = baseTag; + _tagNumber = tagNumber; + _contentStream = contentStream; + _indefiniteLength = contentStream instanceof IndefiniteLengthInputStream; + } + + public boolean isConstructed() + { + return (_baseTag & DERTags.CONSTRUCTED) != 0; + } + + public int getTagNo() + { + return _tagNumber; + } + + public DEREncodable getObjectParser( + int tag, + boolean isExplicit) + throws IOException + { + if (isExplicit) + { + return new ASN1StreamParser(_contentStream).readObject(); + } + + switch (tag) + { + case DERTags.SET: + if (_indefiniteLength) + { + return new BERSetParser(new ASN1StreamParser(_contentStream)); + } + else + { + return new DERSetParser(new ASN1StreamParser(_contentStream)); + } + case DERTags.SEQUENCE: + if (_indefiniteLength) + { + return new BERSequenceParser(new ASN1StreamParser(_contentStream)); + } + else + { + return new DERSequenceParser(new ASN1StreamParser(_contentStream)); + } + case DERTags.OCTET_STRING: + // TODO Is the handling of definite length constructed encodings correct? + if (_indefiniteLength || this.isConstructed()) + { + return new BEROctetStringParser(new ASN1StreamParser(_contentStream)); + } + else + { + return new DEROctetStringParser((DefiniteLengthInputStream)_contentStream); + } + } + + throw new RuntimeException("implicit tagging not implemented"); + } + + private ASN1EncodableVector rLoadVector(InputStream in) + { + try + { + return new ASN1StreamParser(in).readVector(); + } + catch (IOException e) + { + throw new ASN1ParsingException(e.getMessage(), e); + } + } + + public DERObject getDERObject() + { + if (_indefiniteLength) + { + ASN1EncodableVector v = rLoadVector(_contentStream); + + return v.size() == 1 + ? new BERTaggedObject(true, _tagNumber, v.get(0)) + : new BERTaggedObject(false, _tagNumber, BERFactory.createSequence(v)); + } + + if (this.isConstructed()) + { + ASN1EncodableVector v = rLoadVector(_contentStream); + + return v.size() == 1 + ? new DERTaggedObject(true, _tagNumber, v.get(0)) + : new DERTaggedObject(false, _tagNumber, DERFactory.createSequence(v)); + } + + try + { + DefiniteLengthInputStream defIn = (DefiniteLengthInputStream)_contentStream; + return new DERTaggedObject(false, _tagNumber, new DEROctetString(defIn.toByteArray())); + } + catch (IOException e) + { + throw new IllegalStateException(e.getMessage()); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ConstructedOctetStream.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ConstructedOctetStream.java new file mode 100644 index 00000000..045cffda --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/ConstructedOctetStream.java @@ -0,0 +1,111 @@ +package org.bouncycastle.asn1; + +import java.io.InputStream; +import java.io.IOException; + +class ConstructedOctetStream + extends InputStream +{ + private final ASN1StreamParser _parser; + + private boolean _first = true; + private InputStream _currentStream; + + ConstructedOctetStream( + ASN1StreamParser parser) + { + _parser = parser; + } + + public int read(byte[] b, int off, int len) throws IOException + { + if (_currentStream == null) + { + if (!_first) + { + return -1; + } + + ASN1OctetStringParser s = (ASN1OctetStringParser)_parser.readObject(); + + if (s == null) + { + return -1; + } + + _first = false; + _currentStream = s.getOctetStream(); + } + + int totalRead = 0; + + for (;;) + { + int numRead = _currentStream.read(b, off + totalRead, len - totalRead); + + if (numRead >= 0) + { + totalRead += numRead; + + if (totalRead == len) + { + return totalRead; + } + } + else + { + ASN1OctetStringParser aos = (ASN1OctetStringParser)_parser.readObject(); + + if (aos == null) + { + _currentStream = null; + return totalRead < 1 ? -1 : totalRead; + } + + _currentStream = aos.getOctetStream(); + } + } + } + + public int read() + throws IOException + { + if (_currentStream == null) + { + if (!_first) + { + return -1; + } + + ASN1OctetStringParser s = (ASN1OctetStringParser)_parser.readObject(); + + if (s == null) + { + return -1; + } + + _first = false; + _currentStream = s.getOctetStream(); + } + + for (;;) + { + int b = _currentStream.read(); + + if (b >= 0) + { + return b; + } + + ASN1OctetStringParser s = (ASN1OctetStringParser)_parser.readObject(); + + if (s == null) + { + _currentStream = null; + return -1; + } + + _currentStream = s.getOctetStream(); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERApplicationSpecific.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERApplicationSpecific.java new file mode 100644 index 00000000..7ca06d6a --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERApplicationSpecific.java @@ -0,0 +1,225 @@ +package org.bouncycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import org.bouncycastle.util.Arrays; + +/** + * Base class for an application specific object + */ +public class DERApplicationSpecific + extends ASN1Object +{ + private final boolean isConstructed; + private final int tag; + private final byte[] octets; + + DERApplicationSpecific( + boolean isConstructed, + int tag, + byte[] octets) + { + this.isConstructed = isConstructed; + this.tag = tag; + this.octets = octets; + } + + public DERApplicationSpecific( + int tag, + byte[] octets) + { + this(false, tag, octets); + } + + public DERApplicationSpecific( + int tag, + DEREncodable object) + throws IOException + { + this(true, tag, object); + } + + public DERApplicationSpecific( + boolean explicit, + int tag, + DEREncodable object) + throws IOException + { + byte[] data = object.getDERObject().getDEREncoded(); + + this.isConstructed = explicit; + this.tag = tag; + + if (explicit) + { + this.octets = data; + } + else + { + int lenBytes = getLengthOfLength(data); + byte[] tmp = new byte[data.length - lenBytes]; + System.arraycopy(data, lenBytes, tmp, 0, tmp.length); + this.octets = tmp; + } + } + + public DERApplicationSpecific(int tagNo, ASN1EncodableVector vec) + { + this.tag = tagNo; + this.isConstructed = true; + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + for (int i = 0; i != vec.size(); i++) + { + try + { + bOut.write(((ASN1Encodable)vec.get(i)).getEncoded()); + } + catch (IOException e) + { + throw new ASN1ParsingException("malformed object: " + e, e); + } + } + this.octets = bOut.toByteArray(); + } + + private int getLengthOfLength(byte[] data) + { + int count = 2; // TODO: assumes only a 1 byte tag number + + while((data[count - 1] & 0x80) != 0) + { + count++; + } + + return count; + } + + public boolean isConstructed() + { + return isConstructed; + } + + public byte[] getContents() + { + return octets; + } + + public int getApplicationTag() + { + return tag; + } + + /** + * Return the enclosed object assuming explicit tagging. + * + * @return the resulting object + * @throws IOException if reconstruction fails. + */ + public DERObject getObject() + throws IOException + { + return new ASN1InputStream(getContents()).readObject(); + } + + /** + * Return the enclosed object assuming implicit tagging. + * + * @param derTagNo the type tag that should be applied to the object's contents. + * @return the resulting object + * @throws IOException if reconstruction fails. + */ + public DERObject getObject(int derTagNo) + throws IOException + { + if (derTagNo >= 0x1f) + { + throw new IOException("unsupported tag number"); + } + + byte[] orig = this.getEncoded(); + byte[] tmp = replaceTagNumber(derTagNo, orig); + + if ((orig[0] & DERTags.CONSTRUCTED) != 0) + { + tmp[0] |= DERTags.CONSTRUCTED; + } + + return new ASN1InputStream(tmp).readObject(); + } + + /* (non-Javadoc) + * @see org.bouncycastle.asn1.DERObject#encode(org.bouncycastle.asn1.DEROutputStream) + */ + void encode(DEROutputStream out) throws IOException + { + int classBits = DERTags.APPLICATION; + if (isConstructed) + { + classBits |= DERTags.CONSTRUCTED; + } + + out.writeEncoded(classBits, tag, octets); + } + + boolean asn1Equals( + DERObject o) + { + if (!(o instanceof DERApplicationSpecific)) + { + return false; + } + + DERApplicationSpecific other = (DERApplicationSpecific)o; + + return isConstructed == other.isConstructed + && tag == other.tag + && Arrays.areEqual(octets, other.octets); + } + + public int hashCode() + { + return (isConstructed ? 1 : 0) ^ tag ^ Arrays.hashCode(octets); + } + + private byte[] replaceTagNumber(int newTag, byte[] input) + throws IOException + { + int tagNo = input[0] & 0x1f; + int index = 1; + // + // with tagged object tag number is bottom 5 bits, or stored at the start of the content + // + if (tagNo == 0x1f) + { + tagNo = 0; + + int b = input[index++] & 0xff; + + // X.690-0207 8.1.2.4.2 + // "c) bits 7 to 1 of the first subsequent octet shall not all be zero." + if ((b & 0x7f) == 0) // Note: -1 will pass + { + throw new ASN1ParsingException("corrupted stream - invalid high tag number found"); + } + + while ((b >= 0) && ((b & 0x80) != 0)) + { + tagNo |= (b & 0x7f); + tagNo <<= 7; + b = input[index++] & 0xff; + } + + tagNo |= (b & 0x7f); + } + + byte[] tmp = new byte[input.length - index + 1]; + + System.arraycopy(input, index, tmp, 1, tmp.length - 1); + + tmp[0] = (byte)newTag; + + return tmp; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERBMPString.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERBMPString.java new file mode 100644 index 00000000..14723254 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERBMPString.java @@ -0,0 +1,126 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +/** + * DER BMPString object. + */ +public class DERBMPString + extends ASN1Object + implements DERString +{ + String string; + + /** + * return a BMP String from the given object. + * + * @param obj the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERBMPString getInstance( + Object obj) + { + if (obj == null || obj instanceof DERBMPString) + { + return (DERBMPString)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERBMPString(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a BMP String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERBMPString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + + /** + * basic constructor - byte encoded string. + */ + public DERBMPString( + byte[] string) + { + char[] cs = new char[string.length / 2]; + + for (int i = 0; i != cs.length; i++) + { + cs[i] = (char)((string[2 * i] << 8) | (string[2 * i + 1] & 0xff)); + } + + this.string = new String(cs); + } + + /** + * basic constructor + */ + public DERBMPString( + String string) + { + this.string = string; + } + + public String getString() + { + return string; + } + + public String toString() + { + return string; + } + + public int hashCode() + { + return this.getString().hashCode(); + } + + protected boolean asn1Equals( + DERObject o) + { + if (!(o instanceof DERBMPString)) + { + return false; + } + + DERBMPString s = (DERBMPString)o; + + return this.getString().equals(s.getString()); + } + + void encode( + DEROutputStream out) + throws IOException + { + char[] c = string.toCharArray(); + byte[] b = new byte[c.length * 2]; + + for (int i = 0; i != c.length; i++) + { + b[2 * i] = (byte)(c[i] >> 8); + b[2 * i + 1] = (byte)c[i]; + } + + out.writeEncoded(BMP_STRING, b); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERBitString.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERBitString.java new file mode 100644 index 00000000..efcdaca7 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERBitString.java @@ -0,0 +1,266 @@ +package org.bouncycastle.asn1; + +import org.bouncycastle.util.Arrays; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class DERBitString + extends ASN1Object + implements DERString +{ + private static final char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + + protected byte[] data; + protected int padBits; + + /** + * return the correct number of pad bits for a bit string defined in + * a 32 bit constant + */ + static protected int getPadBits( + int bitString) + { + int val = 0; + for (int i = 3; i >= 0; i--) + { + // + // this may look a little odd, but if it isn't done like this pre jdk1.2 + // JVM's break! + // + if (i != 0) + { + if ((bitString >> (i * 8)) != 0) + { + val = (bitString >> (i * 8)) & 0xFF; + break; + } + } + else + { + if (bitString != 0) + { + val = bitString & 0xFF; + break; + } + } + } + + if (val == 0) + { + return 7; + } + + + int bits = 1; + + while (((val <<= 1) & 0xFF) != 0) + { + bits++; + } + + return 8 - bits; + } + + /** + * return the correct number of bytes for a bit string defined in + * a 32 bit constant + */ + static protected byte[] getBytes(int bitString) + { + int bytes = 4; + for (int i = 3; i >= 1; i--) + { + if ((bitString & (0xFF << (i * 8))) != 0) + { + break; + } + bytes--; + } + + byte[] result = new byte[bytes]; + for (int i = 0; i < bytes; i++) + { + result[i] = (byte) ((bitString >> (i * 8)) & 0xFF); + } + + return result; + } + + /** + * return a Bit String from the passed in object + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERBitString getInstance( + Object obj) + { + if (obj == null || obj instanceof DERBitString) + { + return (DERBitString)obj; + } + + if (obj instanceof ASN1OctetString) + { + byte[] bytes = ((ASN1OctetString)obj).getOctets(); + int padBits = bytes[0]; + byte[] data = new byte[bytes.length - 1]; + + System.arraycopy(bytes, 1, data, 0, bytes.length - 1); + + return new DERBitString(data, padBits); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a Bit String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERBitString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + protected DERBitString( + byte data, + int padBits) + { + this.data = new byte[1]; + this.data[0] = data; + this.padBits = padBits; + } + + /** + * @param data the octets making up the bit string. + * @param padBits the number of extra bits at the end of the string. + */ + public DERBitString( + byte[] data, + int padBits) + { + this.data = data; + this.padBits = padBits; + } + + public DERBitString( + byte[] data) + { + this(data, 0); + } + + public DERBitString( + DEREncodable obj) + { + try + { + this.data = obj.getDERObject().getEncoded(ASN1Encodable.DER); + this.padBits = 0; + } + catch (IOException e) + { + throw new IllegalArgumentException("Error processing object : " + e.toString()); + } + } + + public byte[] getBytes() + { + return data; + } + + public int getPadBits() + { + return padBits; + } + + + /** + * @return the value of the bit string as an int (truncating if necessary) + */ + public int intValue() + { + int value = 0; + + for (int i = 0; i != data.length && i != 4; i++) + { + value |= (data[i] & 0xff) << (8 * i); + } + + return value; + } + + void encode( + DEROutputStream out) + throws IOException + { + byte[] bytes = new byte[getBytes().length + 1]; + + bytes[0] = (byte)getPadBits(); + System.arraycopy(getBytes(), 0, bytes, 1, bytes.length - 1); + + out.writeEncoded(BIT_STRING, bytes); + } + + public int hashCode() + { + return padBits ^ Arrays.hashCode(data); + } + + protected boolean asn1Equals( + DERObject o) + { + if (!(o instanceof DERBitString)) + { + return false; + } + + DERBitString other = (DERBitString)o; + + return this.padBits == other.padBits + && Arrays.areEqual(this.data, other.data); + } + + public String getString() + { + StringBuffer buf = new StringBuffer("#"); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ASN1OutputStream aOut = new ASN1OutputStream(bOut); + + try + { + aOut.writeObject(this); + } + catch (IOException e) + { + throw new RuntimeException("internal error encoding BitString"); + } + + byte[] string = bOut.toByteArray(); + + for (int i = 0; i != string.length; i++) + { + buf.append(table[(string[i] >>> 4) & 0xf]); + buf.append(table[string[i] & 0xf]); + } + + return buf.toString(); + } + + public String toString() + { + return getString(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERBoolean.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERBoolean.java new file mode 100644 index 00000000..a2f0c4d4 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERBoolean.java @@ -0,0 +1,113 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +public class DERBoolean + extends ASN1Object +{ + byte value; + + public static final DERBoolean FALSE = new DERBoolean(false); + public static final DERBoolean TRUE = new DERBoolean(true); + + /** + * return a boolean from the passed in object. + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERBoolean getInstance( + Object obj) + { + if (obj == null || obj instanceof DERBoolean) + { + return (DERBoolean)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERBoolean(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a DERBoolean from the passed in boolean. + */ + public static DERBoolean getInstance( + boolean value) + { + return (value ? TRUE : FALSE); + } + + /** + * return a Boolean from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERBoolean getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + public DERBoolean( + byte[] value) + { + this.value = value[0]; + } + + public DERBoolean( + boolean value) + { + this.value = (value) ? (byte)0xff : (byte)0; + } + + public boolean isTrue() + { + return (value != 0); + } + + void encode( + DEROutputStream out) + throws IOException + { + byte[] bytes = new byte[1]; + + bytes[0] = value; + + out.writeEncoded(BOOLEAN, bytes); + } + + protected boolean asn1Equals( + DERObject o) + { + if ((o == null) || !(o instanceof DERBoolean)) + { + return false; + } + + return (value == ((DERBoolean)o).value); + } + + public int hashCode() + { + return value; + } + + + public String toString() + { + return (value != 0) ? "TRUE" : "FALSE"; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERConstructedSequence.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERConstructedSequence.java new file mode 100644 index 00000000..1011cb93 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERConstructedSequence.java @@ -0,0 +1,54 @@ +package org.bouncycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Enumeration; + +/** + * @deprecated use DERSequence. + */ +@SuppressWarnings("unchecked") +public class DERConstructedSequence + extends ASN1Sequence +{ + public void addObject( + DEREncodable obj) + { + super.addObject(obj); + } + + public int getSize() + { + return size(); + } + + /* + * A note on the implementation: + *

+ * As DER requires the constructed, definite-length model to + * be used for structured types, this varies slightly from the + * ASN.1 descriptions given. Rather than just outputing SEQUENCE, + * we also have to specify CONSTRUCTED, and the objects length. + */ + void encode( + DEROutputStream out) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(bOut); + Enumeration e = this.getObjects(); + + while (e.hasMoreElements()) + { + Object obj = e.nextElement(); + + dOut.writeObject(obj); + } + + dOut.close(); + + byte[] bytes = bOut.toByteArray(); + + out.writeEncoded(SEQUENCE | CONSTRUCTED, bytes); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERConstructedSet.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERConstructedSet.java new file mode 100644 index 00000000..656d56b2 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERConstructedSet.java @@ -0,0 +1,80 @@ +package org.bouncycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Enumeration; + +/** + * + * @deprecated use DERSet + */ +@SuppressWarnings("unchecked") +public class DERConstructedSet + extends ASN1Set +{ + public DERConstructedSet() + { + } + + /** + * @param obj - a single object that makes up the set. + */ + public DERConstructedSet( + DEREncodable obj) + { + this.addObject(obj); + } + + /** + * @param v - a vector of objects making up the set. + */ + public DERConstructedSet( + DEREncodableVector v) + { + for (int i = 0; i != v.size(); i++) + { + this.addObject(v.get(i)); + } + } + + public void addObject( + DEREncodable obj) + { + super.addObject(obj); + } + + public int getSize() + { + return size(); + } + + /* + * A note on the implementation: + *

+ * As DER requires the constructed, definite-length model to + * be used for structured types, this varies slightly from the + * ASN.1 descriptions given. Rather than just outputing SET, + * we also have to specify CONSTRUCTED, and the objects length. + */ + void encode( + DEROutputStream out) + throws IOException + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(bOut); + Enumeration e = this.getObjects(); + + while (e.hasMoreElements()) + { + Object obj = e.nextElement(); + + dOut.writeObject(obj); + } + + dOut.close(); + + byte[] bytes = bOut.toByteArray(); + + out.writeEncoded(SET | CONSTRUCTED, bytes); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DEREncodable.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DEREncodable.java new file mode 100644 index 00000000..d89305a1 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DEREncodable.java @@ -0,0 +1,6 @@ +package org.bouncycastle.asn1; + +public interface DEREncodable +{ + public DERObject getDERObject(); +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DEREncodableVector.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DEREncodableVector.java new file mode 100644 index 00000000..86a29450 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DEREncodableVector.java @@ -0,0 +1,39 @@ +package org.bouncycastle.asn1; + +import java.util.Vector; + +/** + * a general class for building up a vector of DER encodable objects - + * this will eventually be superceded by ASN1EncodableVector so you should + * use that class in preference. + */ +@SuppressWarnings("unchecked") +public class DEREncodableVector +{ + Vector v = new Vector(); + + /** + * @deprecated use ASN1EncodableVector instead. + */ + public DEREncodableVector() + { + + } + + public void add( + DEREncodable obj) + { + v.addElement(obj); + } + + public DEREncodable get( + int i) + { + return (DEREncodable)v.elementAt(i); + } + + public int size() + { + return v.size(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DEREnumerated.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DEREnumerated.java new file mode 100644 index 00000000..5a9da4c9 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DEREnumerated.java @@ -0,0 +1,102 @@ +package org.bouncycastle.asn1; + +import org.bouncycastle.util.Arrays; + +import java.io.IOException; +import java.math.BigInteger; + +public class DEREnumerated + extends ASN1Object +{ + byte[] bytes; + + /** + * return an integer from the passed in object + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DEREnumerated getInstance( + Object obj) + { + if (obj == null || obj instanceof DEREnumerated) + { + return (DEREnumerated)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DEREnumerated(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an Enumerated from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DEREnumerated getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + public DEREnumerated( + int value) + { + bytes = BigInteger.valueOf(value).toByteArray(); + } + + public DEREnumerated( + BigInteger value) + { + bytes = value.toByteArray(); + } + + public DEREnumerated( + byte[] bytes) + { + this.bytes = bytes; + } + + public BigInteger getValue() + { + return new BigInteger(bytes); + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(ENUMERATED, bytes); + } + + boolean asn1Equals( + DERObject o) + { + if (!(o instanceof DEREnumerated)) + { + return false; + } + + DEREnumerated other = (DEREnumerated)o; + + return Arrays.areEqual(this.bytes, other.bytes); + } + + public int hashCode() + { + return Arrays.hashCode(bytes); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERExternal.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERExternal.java new file mode 100644 index 00000000..ad062c8b --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERExternal.java @@ -0,0 +1,267 @@ +package org.bouncycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * Class representing the DER-type External + */ +public class DERExternal + extends ASN1Object +{ + private DERObjectIdentifier directReference; + private DERInteger indirectReference; + private ASN1Object dataValueDescriptor; + private int encoding; + private DERObject externalContent; + + public DERExternal(ASN1EncodableVector vector) + { + int offset = 0; + DERObject enc = vector.get(offset).getDERObject(); + if (enc instanceof DERObjectIdentifier) + { + directReference = (DERObjectIdentifier)enc; + offset++; + enc = vector.get(offset).getDERObject(); + } + if (enc instanceof DERInteger) + { + indirectReference = (DERInteger) enc; + offset++; + enc = vector.get(offset).getDERObject(); + } + if (!(enc instanceof DERTaggedObject)) + { + dataValueDescriptor = (ASN1Object) enc; + offset++; + enc = vector.get(offset).getDERObject(); + } + if (!(enc instanceof DERTaggedObject)) + { + throw new IllegalArgumentException("No tagged object found in vector. Structure doesn't seem to be of type External"); + } + DERTaggedObject obj = (DERTaggedObject)enc; + setEncoding(obj.getTagNo()); + externalContent = obj.getObject(); + } + + /** + * Creates a new instance of DERExternal + * See X.690 for more informations about the meaning of these parameters + * @param directReference The direct reference or null if not set. + * @param indirectReference The indirect reference or null if not set. + * @param dataValueDescriptor The data value descriptor or null if not set. + * @param externalData The external data in its encoded form. + */ + public DERExternal(DERObjectIdentifier directReference, DERInteger indirectReference, ASN1Object dataValueDescriptor, DERTaggedObject externalData) + { + this(directReference, indirectReference, dataValueDescriptor, externalData.getTagNo(), externalData.getDERObject()); + } + + /** + * Creates a new instance of DERExternal. + * See X.690 for more informations about the meaning of these parameters + * @param directReference The direct reference or null if not set. + * @param indirectReference The indirect reference or null if not set. + * @param dataValueDescriptor The data value descriptor or null if not set. + * @param encoding The encoding to be used for the external data + * @param externalData The external data + */ + public DERExternal(DERObjectIdentifier directReference, DERInteger indirectReference, ASN1Object dataValueDescriptor, int encoding, DERObject externalData) + { + setDirectReference(directReference); + setIndirectReference(indirectReference); + setDataValueDescriptor(dataValueDescriptor); + setEncoding(encoding); + setExternalContent(externalData.getDERObject()); + } + + /* (non-Javadoc) + * @see java.lang.Object#hashCode() + */ + public int hashCode() + { + int ret = 0; + if (directReference != null) + { + ret = directReference.hashCode(); + } + if (indirectReference != null) + { + ret ^= indirectReference.hashCode(); + } + if (dataValueDescriptor != null) + { + ret ^= dataValueDescriptor.hashCode(); + } + ret ^= externalContent.hashCode(); + return ret; + } + + /* (non-Javadoc) + * @see org.bouncycastle.asn1.DERObject#encode(org.bouncycastle.asn1.DEROutputStream) + */ + void encode(DEROutputStream out) + throws IOException + { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + if (directReference != null) + { + baos.write(directReference.getDEREncoded()); + } + if (indirectReference != null) + { + baos.write(indirectReference.getDEREncoded()); + } + if (dataValueDescriptor != null) + { + baos.write(dataValueDescriptor.getDEREncoded()); + } + DERTaggedObject obj = new DERTaggedObject(encoding, externalContent); + baos.write(obj.getDEREncoded()); + out.writeEncoded(DERTags.CONSTRUCTED, DERTags.EXTERNAL, baos.toByteArray()); + } + + /* (non-Javadoc) + * @see org.bouncycastle.asn1.ASN1Object#asn1Equals(org.bouncycastle.asn1.DERObject) + */ + boolean asn1Equals(DERObject o) + { + if (!(o instanceof DERExternal)) + { + return false; + } + if (this == o) + { + return true; + } + DERExternal other = (DERExternal)o; + if (directReference != null) + { + if (other.directReference == null || !other.directReference.equals(directReference)) + { + return false; + } + } + if (indirectReference != null) + { + if (other.indirectReference == null || !other.indirectReference.equals(indirectReference)) + { + return false; + } + } + if (dataValueDescriptor != null) + { + if (other.dataValueDescriptor == null || !other.dataValueDescriptor.equals(dataValueDescriptor)) + { + return false; + } + } + return externalContent.equals(other.externalContent); + } + + /** + * Returns the data value descriptor + * @return The descriptor + */ + public ASN1Object getDataValueDescriptor() + { + return dataValueDescriptor; + } + + /** + * Returns the direct reference of the external element + * @return The reference + */ + public DERObjectIdentifier getDirectReference() + { + return directReference; + } + + /** + * Returns the encoding of the content. Valid values are + *

    + *
  • 0 single-ASN1-type
  • + *
  • 1 OCTET STRING
  • + *
  • 2 BIT STRING
  • + *
+ * @return The encoding + */ + public int getEncoding() + { + return encoding; + } + + /** + * Returns the content of this element + * @return The content + */ + public DERObject getExternalContent() + { + return externalContent; + } + + /** + * Returns the indirect reference of this element + * @return The reference + */ + public DERInteger getIndirectReference() + { + return indirectReference; + } + + /** + * Sets the data value descriptor + * @param dataValueDescriptor The descriptor + */ + private void setDataValueDescriptor(ASN1Object dataValueDescriptor) + { + this.dataValueDescriptor = dataValueDescriptor; + } + + /** + * Sets the direct reference of the external element + * @param directReferemce The reference + */ + private void setDirectReference(DERObjectIdentifier directReferemce) + { + this.directReference = directReferemce; + } + + /** + * Sets the encoding of the content. Valid values are + *
    + *
  • 0 single-ASN1-type
  • + *
  • 1 OCTET STRING
  • + *
  • 2 BIT STRING
  • + *
+ * @param encoding The encoding + */ + private void setEncoding(int encoding) + { + if (encoding < 0 || encoding > 2) + { + throw new IllegalArgumentException("invalid encoding value: " + encoding); + } + this.encoding = encoding; + } + + /** + * Sets the content of this element + * @param externalContent The content + */ + private void setExternalContent(DERObject externalContent) + { + this.externalContent = externalContent; + } + + /** + * Sets the indirect reference of this element + * @param indirectReference The reference + */ + private void setIndirectReference(DERInteger indirectReference) + { + this.indirectReference = indirectReference; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERExternalParser.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERExternalParser.java new file mode 100644 index 00000000..0fbfb680 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERExternalParser.java @@ -0,0 +1,39 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +public class DERExternalParser + implements DEREncodable +{ + private ASN1StreamParser _parser; + + /** + * + */ + public DERExternalParser(ASN1StreamParser parser) + { + this._parser = parser; + } + + public DEREncodable readObject() + throws IOException + { + return _parser.readObject(); + } + + public DERObject getDERObject() + { + try + { + return new DERExternal(_parser.readVector()); + } + catch (IOException ioe) + { + throw new ASN1ParsingException("unable to get DER object", ioe); + } + catch (IllegalArgumentException ioe) + { + throw new ASN1ParsingException("unable to get DER object", ioe); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERFactory.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERFactory.java new file mode 100644 index 00000000..7364282e --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERFactory.java @@ -0,0 +1,22 @@ +package org.bouncycastle.asn1; + +class DERFactory +{ + static final DERSequence EMPTY_SEQUENCE = new DERSequence(); + static final DERSet EMPTY_SET = new DERSet(); + + static DERSequence createSequence(ASN1EncodableVector v) + { + return v.size() < 1 ? EMPTY_SEQUENCE : new DERSequence(v); + } + + static DERSet createSet(ASN1EncodableVector v) + { + return v.size() < 1 ? EMPTY_SET : new DERSet(v); + } + + static DERSet createSet(ASN1EncodableVector v, boolean needsSorting) + { + return v.size() < 1 ? EMPTY_SET : new DERSet(v, needsSorting); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERGeneralString.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERGeneralString.java new file mode 100644 index 00000000..1992cf34 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERGeneralString.java @@ -0,0 +1,92 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +public class DERGeneralString + extends ASN1Object implements DERString +{ + private String string; + + public static DERGeneralString getInstance( + Object obj) + { + if (obj == null || obj instanceof DERGeneralString) + { + return (DERGeneralString) obj; + } + if (obj instanceof ASN1OctetString) + { + return new DERGeneralString(((ASN1OctetString) obj).getOctets()); + } + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject) obj).getObject()); + } + throw new IllegalArgumentException("illegal object in getInstance: " + + obj.getClass().getName()); + } + + public static DERGeneralString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + public DERGeneralString(byte[] string) + { + char[] cs = new char[string.length]; + for (int i = 0; i != cs.length; i++) + { + cs[i] = (char)(string[i] & 0xff); + } + this.string = new String(cs); + } + + public DERGeneralString(String string) + { + this.string = string; + } + + public String getString() + { + return string; + } + + public String toString() + { + return string; + } + + public byte[] getOctets() + { + char[] cs = string.toCharArray(); + byte[] bs = new byte[cs.length]; + for (int i = 0; i != cs.length; i++) + { + bs[i] = (byte) cs[i]; + } + return bs; + } + + void encode(DEROutputStream out) + throws IOException + { + out.writeEncoded(GENERAL_STRING, this.getOctets()); + } + + public int hashCode() + { + return this.getString().hashCode(); + } + + boolean asn1Equals(DERObject o) + { + if (!(o instanceof DERGeneralString)) + { + return false; + } + DERGeneralString s = (DERGeneralString) o; + return this.getString().equals(s.getString()); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERGeneralizedTime.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERGeneralizedTime.java new file mode 100644 index 00000000..53663478 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERGeneralizedTime.java @@ -0,0 +1,314 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.SimpleTimeZone; +import java.util.TimeZone; + +/** + * Generalized time object. + */ +public class DERGeneralizedTime + extends ASN1Object +{ + String time; + + /** + * return a generalized time from the passed in object + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERGeneralizedTime getInstance( + Object obj) + { + if (obj == null || obj instanceof DERGeneralizedTime) + { + return (DERGeneralizedTime)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERGeneralizedTime(((ASN1OctetString)obj).getOctets()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a Generalized Time object from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERGeneralizedTime getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + /** + * The correct format for this is YYYYMMDDHHMMSS[.f]Z, or without the Z + * for local time, or Z+-HHMM on the end, for difference between local + * time and UTC time. The fractional second amount f must consist of at + * least one number with trailing zeroes removed. + * + * @param time the time string. + * @exception IllegalArgumentException if String is an illegal format. + */ + public DERGeneralizedTime( + String time) + { + this.time = time; + try + { + this.getDate(); + } + catch (ParseException e) + { + throw new IllegalArgumentException("invalid date string: " + e.getMessage()); + } + } + + /** + * base constructer from a java.util.date object + */ + public DERGeneralizedTime( + Date time) + { + SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'"); + + dateF.setTimeZone(new SimpleTimeZone(0,"Z")); + + this.time = dateF.format(time); + } + + DERGeneralizedTime( + byte[] bytes) + { + // + // explicitly convert to characters + // + char[] dateC = new char[bytes.length]; + + for (int i = 0; i != dateC.length; i++) + { + dateC[i] = (char)(bytes[i] & 0xff); + } + + this.time = new String(dateC); + } + + /** + * Return the time. + * @return The time string as it appeared in the encoded object. + */ + public String getTimeString() + { + return time; + } + + /** + * return the time - always in the form of + * YYYYMMDDhhmmssGMT(+hh:mm|-hh:mm). + *

+ * Normally in a certificate we would expect "Z" rather than "GMT", + * however adding the "GMT" means we can just use: + *

+     *     dateF = new SimpleDateFormat("yyyyMMddHHmmssz");
+     * 
+ * To read in the time and get a date which is compatible with our local + * time zone. + */ + public String getTime() + { + // + // standardise the format. + // + if (time.charAt(time.length() - 1) == 'Z') + { + return time.substring(0, time.length() - 1) + "GMT+00:00"; + } + else + { + int signPos = time.length() - 5; + char sign = time.charAt(signPos); + if (sign == '-' || sign == '+') + { + return time.substring(0, signPos) + + "GMT" + + time.substring(signPos, signPos + 3) + + ":" + + time.substring(signPos + 3); + } + else + { + signPos = time.length() - 3; + sign = time.charAt(signPos); + if (sign == '-' || sign == '+') + { + return time.substring(0, signPos) + + "GMT" + + time.substring(signPos) + + ":00"; + } + } + } + return time + calculateGMTOffset(); + } + + private String calculateGMTOffset() + { + String sign = "+"; + TimeZone timeZone = TimeZone.getDefault(); + int offset = timeZone.getRawOffset(); + if (offset < 0) + { + sign = "-"; + offset = -offset; + } + int hours = offset / (60 * 60 * 1000); + int minutes = (offset - (hours * 60 * 60 * 1000)) / (60 * 1000); + + try + { + if (timeZone.useDaylightTime() && timeZone.inDaylightTime(this.getDate())) + { + hours += sign.equals("+") ? 1 : -1; + } + } + catch (ParseException e) + { + // we'll do our best and ignore daylight savings + } + + return "GMT" + sign + convert(hours) + ":" + convert(minutes); + } + + private String convert(int time) + { + if (time < 10) + { + return "0" + time; + } + + return Integer.toString(time); + } + + public Date getDate() + throws ParseException + { + SimpleDateFormat dateF; + String d = time; + + if (time.endsWith("Z")) + { + if (hasFractionalSeconds()) + { + dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS'Z'"); + } + else + { + dateF = new SimpleDateFormat("yyyyMMddHHmmss'Z'"); + } + + dateF.setTimeZone(new SimpleTimeZone(0, "Z")); + } + else if (time.indexOf('-') > 0 || time.indexOf('+') > 0) + { + d = this.getTime(); + if (hasFractionalSeconds()) + { + dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSSz"); + } + else + { + dateF = new SimpleDateFormat("yyyyMMddHHmmssz"); + } + + dateF.setTimeZone(new SimpleTimeZone(0, "Z")); + } + else + { + if (hasFractionalSeconds()) + { + dateF = new SimpleDateFormat("yyyyMMddHHmmss.SSS"); + } + else + { + dateF = new SimpleDateFormat("yyyyMMddHHmmss"); + } + + dateF.setTimeZone(new SimpleTimeZone(0, TimeZone.getDefault().getID())); + } + + if (hasFractionalSeconds()) + { + // java misinterprets extra digits as being milliseconds... + String frac = d.substring(14); + int index; + for (index = 1; index < frac.length(); index++) + { + char ch = frac.charAt(index); + if (!('0' <= ch && ch <= '9')) + { + break; + } + } + if (index - 1 > 3) + { + frac = frac.substring(0, 4) + frac.substring(index); + d = d.substring(0, 14) + frac; + } + } + + return dateF.parse(d); + } + + private boolean hasFractionalSeconds() + { + return time.indexOf('.') == 14; + } + + private byte[] getOctets() + { + char[] cs = time.toCharArray(); + byte[] bs = new byte[cs.length]; + + for (int i = 0; i != cs.length; i++) + { + bs[i] = (byte)cs[i]; + } + + return bs; + } + + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(GENERALIZED_TIME, this.getOctets()); + } + + boolean asn1Equals( + DERObject o) + { + if (!(o instanceof DERGeneralizedTime)) + { + return false; + } + + return time.equals(((DERGeneralizedTime)o).time); + } + + public int hashCode() + { + return time.hashCode(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERGenerator.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERGenerator.java new file mode 100644 index 00000000..8aef7a74 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERGenerator.java @@ -0,0 +1,119 @@ +package org.bouncycastle.asn1; + +import org.bouncycastle.util.io.Streams; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public abstract class DERGenerator + extends ASN1Generator +{ + private boolean _tagged = false; + private boolean _isExplicit; + private int _tagNo; + + protected DERGenerator( + OutputStream out) + { + super(out); + } + + public DERGenerator( + OutputStream out, + int tagNo, + boolean isExplicit) + { + super(out); + + _tagged = true; + _isExplicit = isExplicit; + _tagNo = tagNo; + } + + private void writeLength( + OutputStream out, + int length) + throws IOException + { + if (length > 127) + { + int size = 1; + int val = length; + + while ((val >>>= 8) != 0) + { + size++; + } + + out.write((byte)(size | 0x80)); + + for (int i = (size - 1) * 8; i >= 0; i -= 8) + { + out.write((byte)(length >> i)); + } + } + else + { + out.write((byte)length); + } + } + + void writeDEREncoded( + OutputStream out, + int tag, + byte[] bytes) + throws IOException + { + out.write(tag); + writeLength(out, bytes.length); + out.write(bytes); + } + + void writeDEREncoded( + int tag, + byte[] bytes) + throws IOException + { + if (_tagged) + { + int tagNum = _tagNo | DERTags.TAGGED; + + if (_isExplicit) + { + int newTag = _tagNo | DERTags.CONSTRUCTED | DERTags.TAGGED; + + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + writeDEREncoded(bOut, tag, bytes); + + writeDEREncoded(_out, newTag, bOut.toByteArray()); + } + else + { + if ((tag & DERTags.CONSTRUCTED) != 0) + { + writeDEREncoded(_out, tagNum | DERTags.CONSTRUCTED, bytes); + } + else + { + writeDEREncoded(_out, tagNum, bytes); + } + } + } + else + { + writeDEREncoded(_out, tag, bytes); + } + } + + void writeDEREncoded( + OutputStream out, + int tag, + InputStream in) + throws IOException + { + writeDEREncoded(out, tag, Streams.readAll(in)); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERIA5String.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERIA5String.java new file mode 100644 index 00000000..a90830c1 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERIA5String.java @@ -0,0 +1,174 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +/** + * DER IA5String object - this is an ascii string. + */ +public class DERIA5String + extends ASN1Object + implements DERString +{ + String string; + + /** + * return a IA5 string from the passed in object + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERIA5String getInstance( + Object obj) + { + if (obj == null || obj instanceof DERIA5String) + { + return (DERIA5String)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERIA5String(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an IA5 String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERIA5String getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + /** + * basic constructor - with bytes. + */ + public DERIA5String( + byte[] string) + { + char[] cs = new char[string.length]; + + for (int i = 0; i != cs.length; i++) + { + cs[i] = (char)(string[i] & 0xff); + } + + this.string = new String(cs); + } + + /** + * basic constructor - without validation. + */ + public DERIA5String( + String string) + { + this(string, false); + } + + /** + * Constructor with optional validation. + * + * @param string the base string to wrap. + * @param validate whether or not to check the string. + * @throws IllegalArgumentException if validate is true and the string + * contains characters that should not be in an IA5String. + */ + public DERIA5String( + String string, + boolean validate) + { + if (string == null) + { + throw new NullPointerException("string cannot be null"); + } + if (validate && !isIA5String(string)) + { + throw new IllegalArgumentException("string contains illegal characters"); + } + + this.string = string; + } + + public String getString() + { + return string; + } + + public String toString() + { + return string; + } + + public byte[] getOctets() + { + char[] cs = string.toCharArray(); + byte[] bs = new byte[cs.length]; + + for (int i = 0; i != cs.length; i++) + { + bs[i] = (byte)cs[i]; + } + + return bs; + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(IA5_STRING, this.getOctets()); + } + + public int hashCode() + { + return this.getString().hashCode(); + } + + boolean asn1Equals( + DERObject o) + { + if (!(o instanceof DERIA5String)) + { + return false; + } + + DERIA5String s = (DERIA5String)o; + + return this.getString().equals(s.getString()); + } + + /** + * return true if the passed in String can be represented without + * loss as an IA5String, false otherwise. + * + * @return true if in printable set, false otherwise. + */ + public static boolean isIA5String( + String str) + { + for (int i = str.length() - 1; i >= 0; i--) + { + char ch = str.charAt(i); + + if (ch > 0x007f) + { + return false; + } + } + + return true; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERInputStream.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERInputStream.java new file mode 100644 index 00000000..51f55055 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERInputStream.java @@ -0,0 +1,272 @@ +package org.bouncycastle.asn1; + +import java.io.ByteArrayInputStream; +import java.io.EOFException; +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; + +/** + * Don't use this class. It will eventually disappear, use ASN1InputStream. + *
+ * This class is scheduled for removal. + * @deprecated use ASN1InputStream + */ +public class DERInputStream + extends FilterInputStream implements DERTags +{ + /** + * @deprecated use ASN1InputStream + */ + public DERInputStream( + InputStream is) + { + super(is); + } + + protected int readLength() + throws IOException + { + int length = read(); + if (length < 0) + { + throw new IOException("EOF found when length expected"); + } + + if (length == 0x80) + { + return -1; // indefinite-length encoding + } + + if (length > 127) + { + int size = length & 0x7f; + + if (size > 4) + { + throw new IOException("DER length more than 4 bytes"); + } + + length = 0; + for (int i = 0; i < size; i++) + { + int next = read(); + + if (next < 0) + { + throw new IOException("EOF found reading length"); + } + + length = (length << 8) + next; + } + + if (length < 0) + { + throw new IOException("corrupted stream - negative length found"); + } + } + + return length; + } + + protected void readFully( + byte[] bytes) + throws IOException + { + int left = bytes.length; + + if (left == 0) + { + return; + } + + while (left > 0) + { + int l = read(bytes, bytes.length - left, left); + + if (l < 0) + { + throw new EOFException("unexpected end of stream"); + } + + left -= l; + } + } + + /** + * build an object given its tag and a byte stream to construct it + * from. + */ + protected DERObject buildObject( + int tag, + byte[] bytes) + throws IOException + { + switch (tag) + { + case NULL: + return null; + case SEQUENCE | CONSTRUCTED: + ByteArrayInputStream bIn = new ByteArrayInputStream(bytes); + BERInputStream dIn = new BERInputStream(bIn); + DERConstructedSequence seq = new DERConstructedSequence(); + + try + { + for (;;) + { + DERObject obj = dIn.readObject(); + + seq.addObject(obj); + } + } + catch (EOFException ex) + { + return seq; + } + case SET | CONSTRUCTED: + bIn = new ByteArrayInputStream(bytes); + dIn = new BERInputStream(bIn); + + ASN1EncodableVector v = new ASN1EncodableVector(); + + try + { + for (;;) + { + DERObject obj = dIn.readObject(); + + v.add(obj); + } + } + catch (EOFException ex) + { + return new DERConstructedSet(v); + } + case BOOLEAN: + return new DERBoolean(bytes); + case INTEGER: + return new DERInteger(bytes); + case ENUMERATED: + return new DEREnumerated(bytes); + case OBJECT_IDENTIFIER: + return new DERObjectIdentifier(bytes); + case BIT_STRING: + int padBits = bytes[0]; + byte[] data = new byte[bytes.length - 1]; + + System.arraycopy(bytes, 1, data, 0, bytes.length - 1); + + return new DERBitString(data, padBits); + case UTF8_STRING: + return new DERUTF8String(bytes); + case PRINTABLE_STRING: + return new DERPrintableString(bytes); + case IA5_STRING: + return new DERIA5String(bytes); + case T61_STRING: + return new DERT61String(bytes); + case VISIBLE_STRING: + return new DERVisibleString(bytes); + case UNIVERSAL_STRING: + return new DERUniversalString(bytes); + case GENERAL_STRING: + return new DERGeneralString(bytes); + case BMP_STRING: + return new DERBMPString(bytes); + case OCTET_STRING: + return new DEROctetString(bytes); + case UTC_TIME: + return new DERUTCTime(bytes); + case GENERALIZED_TIME: + return new DERGeneralizedTime(bytes); + default: + // + // with tagged object tag number is bottom 5 bits + // + if ((tag & TAGGED) != 0) + { + if ((tag & 0x1f) == 0x1f) + { + throw new IOException("unsupported high tag encountered"); + } + + if (bytes.length == 0) // empty tag! + { + if ((tag & CONSTRUCTED) == 0) + { + return new DERTaggedObject(false, tag & 0x1f, new DERNull()); + } + else + { + return new DERTaggedObject(false, tag & 0x1f, new DERConstructedSequence()); + } + } + + // + // simple type - implicit... return an octet string + // + if ((tag & CONSTRUCTED) == 0) + { + return new DERTaggedObject(false, tag & 0x1f, new DEROctetString(bytes)); + } + + bIn = new ByteArrayInputStream(bytes); + dIn = new BERInputStream(bIn); + + DEREncodable dObj = dIn.readObject(); + + // + // explicitly tagged (probably!) - if it isn't we'd have to + // tell from the context + // + if (dIn.available() == 0) + { + return new DERTaggedObject(tag & 0x1f, dObj); + } + + // + // another implicit object, we'll create a sequence... + // + seq = new DERConstructedSequence(); + + seq.addObject(dObj); + + try + { + for (;;) + { + dObj = dIn.readObject(); + + seq.addObject(dObj); + } + } + catch (EOFException ex) + { + // ignore -- + } + + return new DERTaggedObject(false, tag & 0x1f, seq); + } + + return new DERUnknownTag(tag, bytes); + } + } + + public DERObject readObject() + throws IOException + { + int tag = read(); + if (tag == -1) + { + throw new EOFException(); + } + + int length = readLength(); + byte[] bytes = new byte[length]; + + readFully(bytes); + + return buildObject(tag, bytes); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERInteger.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERInteger.java new file mode 100644 index 00000000..8f97428b --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERInteger.java @@ -0,0 +1,123 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; +import java.math.BigInteger; + +import org.bouncycastle.util.Arrays; + +public class DERInteger + extends ASN1Object +{ + byte[] bytes; + + /** + * return an integer from the passed in object + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERInteger getInstance( + Object obj) + { + if (obj == null || obj instanceof DERInteger) + { + return (DERInteger)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERInteger(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an Integer from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERInteger getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + public DERInteger( + int value) + { + bytes = BigInteger.valueOf(value).toByteArray(); + } + + public DERInteger( + BigInteger value) + { + bytes = value.toByteArray(); + } + + public DERInteger( + byte[] bytes) + { + this.bytes = bytes; + } + + public BigInteger getValue() + { + return new BigInteger(bytes); + } + + /** + * in some cases positive values get crammed into a space, + * that's not quite big enough... + */ + public BigInteger getPositiveValue() + { + return new BigInteger(1, bytes); + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(INTEGER, bytes); + } + + public int hashCode() + { + int value = 0; + + for (int i = 0; i != bytes.length; i++) + { + value ^= (bytes[i] & 0xff) << (i % 4); + } + + return value; + } + + boolean asn1Equals( + DERObject o) + { + if (!(o instanceof DERInteger)) + { + return false; + } + + DERInteger other = (DERInteger)o; + + return Arrays.areEqual(bytes, other.bytes); + } + + public String toString() + { + return getValue().toString(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERNull.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERNull.java new file mode 100644 index 00000000..5d020c7d --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERNull.java @@ -0,0 +1,25 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +/** + * A NULL object. + */ +public class DERNull + extends ASN1Null +{ + public static final DERNull INSTANCE = new DERNull(); + + byte[] zeroBytes = new byte[0]; + + public DERNull() + { + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(NULL, zeroBytes); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERNumericString.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERNumericString.java new file mode 100644 index 00000000..3c721936 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERNumericString.java @@ -0,0 +1,177 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +/** + * DER NumericString object - this is an ascii string of characters {0,1,2,3,4,5,6,7,8,9, }. + */ +public class DERNumericString + extends ASN1Object + implements DERString +{ + String string; + + /** + * return a Numeric string from the passed in object + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERNumericString getInstance( + Object obj) + { + if (obj == null || obj instanceof DERNumericString) + { + return (DERNumericString)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERNumericString(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an Numeric String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERNumericString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + /** + * basic constructor - with bytes. + */ + public DERNumericString( + byte[] string) + { + char[] cs = new char[string.length]; + + for (int i = 0; i != cs.length; i++) + { + cs[i] = (char)(string[i] & 0xff); + } + + this.string = new String(cs); + } + + /** + * basic constructor - without validation.. + */ + public DERNumericString( + String string) + { + this(string, false); + } + + /** + * Constructor with optional validation. + * + * @param string the base string to wrap. + * @param validate whether or not to check the string. + * @throws IllegalArgumentException if validate is true and the string + * contains characters that should not be in a NumericString. + */ + public DERNumericString( + String string, + boolean validate) + { + if (validate && !isNumericString(string)) + { + throw new IllegalArgumentException("string contains illegal characters"); + } + + this.string = string; + } + + public String getString() + { + return string; + } + + public String toString() + { + return string; + } + + public byte[] getOctets() + { + char[] cs = string.toCharArray(); + byte[] bs = new byte[cs.length]; + + for (int i = 0; i != cs.length; i++) + { + bs[i] = (byte)cs[i]; + } + + return bs; + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(NUMERIC_STRING, this.getOctets()); + } + + public int hashCode() + { + return this.getString().hashCode(); + } + + boolean asn1Equals( + DERObject o) + { + if (!(o instanceof DERNumericString)) + { + return false; + } + + DERNumericString s = (DERNumericString)o; + + return this.getString().equals(s.getString()); + } + + /** + * Return true if the string can be represented as a NumericString ('0'..'9', ' ') + * + * @param str string to validate. + * @return true if numeric, fale otherwise. + */ + public static boolean isNumericString( + String str) + { + for (int i = str.length() - 1; i >= 0; i--) + { + char ch = str.charAt(i); + + if (ch > 0x007f) + { + return false; + } + + if (('0' <= ch && ch <= '9') || ch == ' ') + { + continue; + } + + return false; + } + + return true; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERObject.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERObject.java new file mode 100644 index 00000000..42e24878 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERObject.java @@ -0,0 +1,20 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +public abstract class DERObject + extends ASN1Encodable + implements DERTags +{ + public DERObject toASN1Object() + { + return this; + } + + public abstract int hashCode(); + + public abstract boolean equals(Object o); + + abstract void encode(DEROutputStream out) + throws IOException; +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERObjectIdentifier.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERObjectIdentifier.java new file mode 100644 index 00000000..1ba6b2d1 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERObjectIdentifier.java @@ -0,0 +1,293 @@ +package org.bouncycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.math.BigInteger; + +public class DERObjectIdentifier + extends ASN1Object +{ + String identifier; + + /** + * return an OID from the passed in object + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERObjectIdentifier getInstance( + Object obj) + { + if (obj == null || obj instanceof DERObjectIdentifier) + { + return (DERObjectIdentifier)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERObjectIdentifier(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an Object Identifier from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERObjectIdentifier getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + + DERObjectIdentifier( + byte[] bytes) + { + StringBuffer objId = new StringBuffer(); + long value = 0; + BigInteger bigValue = null; + boolean first = true; + + for (int i = 0; i != bytes.length; i++) + { + int b = bytes[i] & 0xff; + + if (value < 0x80000000000000L) + { + value = value * 128 + (b & 0x7f); + if ((b & 0x80) == 0) // end of number reached + { + if (first) + { + switch ((int)value / 40) + { + case 0: + objId.append('0'); + break; + case 1: + objId.append('1'); + value -= 40; + break; + default: + objId.append('2'); + value -= 80; + } + first = false; + } + + objId.append('.'); + objId.append(value); + value = 0; + } + } + else + { + if (bigValue == null) + { + bigValue = BigInteger.valueOf(value); + } + bigValue = bigValue.shiftLeft(7); + bigValue = bigValue.or(BigInteger.valueOf(b & 0x7f)); + if ((b & 0x80) == 0) + { + objId.append('.'); + objId.append(bigValue); + bigValue = null; + value = 0; + } + } + } + + this.identifier = objId.toString(); + } + + public DERObjectIdentifier( + String identifier) + { + if (!isValidIdentifier(identifier)) + { + throw new IllegalArgumentException("string " + identifier + " not an OID"); + } + + this.identifier = identifier; + } + + public String getId() + { + return identifier; + } + + private void writeField( + OutputStream out, + long fieldValue) + throws IOException + { + if (fieldValue >= (1L << 7)) + { + if (fieldValue >= (1L << 14)) + { + if (fieldValue >= (1L << 21)) + { + if (fieldValue >= (1L << 28)) + { + if (fieldValue >= (1L << 35)) + { + if (fieldValue >= (1L << 42)) + { + if (fieldValue >= (1L << 49)) + { + if (fieldValue >= (1L << 56)) + { + out.write((int)(fieldValue >> 56) | 0x80); + } + out.write((int)(fieldValue >> 49) | 0x80); + } + out.write((int)(fieldValue >> 42) | 0x80); + } + out.write((int)(fieldValue >> 35) | 0x80); + } + out.write((int)(fieldValue >> 28) | 0x80); + } + out.write((int)(fieldValue >> 21) | 0x80); + } + out.write((int)(fieldValue >> 14) | 0x80); + } + out.write((int)(fieldValue >> 7) | 0x80); + } + out.write((int)fieldValue & 0x7f); + } + + private void writeField( + OutputStream out, + BigInteger fieldValue) + throws IOException + { + int byteCount = (fieldValue.bitLength()+6)/7; + if (byteCount == 0) + { + out.write(0); + } + else + { + BigInteger tmpValue = fieldValue; + byte[] tmp = new byte[byteCount]; + for (int i = byteCount-1; i >= 0; i--) + { + tmp[i] = (byte) ((tmpValue.intValue() & 0x7f) | 0x80); + tmpValue = tmpValue.shiftRight(7); + } + tmp[byteCount-1] &= 0x7f; + out.write(tmp); + } + + } + + void encode( + DEROutputStream out) + throws IOException + { + OIDTokenizer tok = new OIDTokenizer(identifier); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(bOut); + + writeField(bOut, + Integer.parseInt(tok.nextToken()) * 40 + + Integer.parseInt(tok.nextToken())); + + while (tok.hasMoreTokens()) + { + String token = tok.nextToken(); + if (token.length() < 18) + { + writeField(bOut, Long.parseLong(token)); + } + else + { + writeField(bOut, new BigInteger(token)); + } + } + + dOut.close(); + + byte[] bytes = bOut.toByteArray(); + + out.writeEncoded(OBJECT_IDENTIFIER, bytes); + } + + public int hashCode() + { + return identifier.hashCode(); + } + + boolean asn1Equals( + DERObject o) + { + if (!(o instanceof DERObjectIdentifier)) + { + return false; + } + + return identifier.equals(((DERObjectIdentifier)o).identifier); + } + + public String toString() + { + return getId(); + } + + private static boolean isValidIdentifier( + String identifier) + { + if (identifier.length() < 3 + || identifier.charAt(1) != '.') + { + return false; + } + + char first = identifier.charAt(0); + if (first < '0' || first > '2') + { + return false; + } + + boolean periodAllowed = false; + for (int i = identifier.length() - 1; i >= 2; i--) + { + char ch = identifier.charAt(i); + + if ('0' <= ch && ch <= '9') + { + periodAllowed = true; + continue; + } + + if (ch == '.') + { + if (!periodAllowed) + { + return false; + } + + periodAllowed = false; + continue; + } + + return false; + } + + return periodAllowed; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DEROctetString.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DEROctetString.java new file mode 100644 index 00000000..cadd60c8 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DEROctetString.java @@ -0,0 +1,37 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +public class DEROctetString + extends ASN1OctetString +{ + /** + * @param string the octets making up the octet string. + */ + public DEROctetString( + byte[] string) + { + super(string); + } + + public DEROctetString( + DEREncodable obj) + { + super(obj); + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(OCTET_STRING, string); + } + + static void encode( + DEROutputStream derOut, + byte[] bytes) + throws IOException + { + derOut.writeEncoded(DERTags.OCTET_STRING, bytes); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DEROctetStringParser.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DEROctetStringParser.java new file mode 100644 index 00000000..f6138d97 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DEROctetStringParser.java @@ -0,0 +1,33 @@ +package org.bouncycastle.asn1; + +import java.io.InputStream; +import java.io.IOException; + +public class DEROctetStringParser + implements ASN1OctetStringParser +{ + private DefiniteLengthInputStream stream; + + DEROctetStringParser( + DefiniteLengthInputStream stream) + { + this.stream = stream; + } + + public InputStream getOctetStream() + { + return stream; + } + + public DERObject getDERObject() + { + try + { + return new DEROctetString(stream.toByteArray()); + } + catch (IOException e) + { + throw new ASN1ParsingException("IOException converting stream to byte array: " + e.getMessage(), e); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DEROutputStream.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DEROutputStream.java new file mode 100644 index 00000000..b78f7caf --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DEROutputStream.java @@ -0,0 +1,134 @@ +package org.bouncycastle.asn1; + +import java.io.FilterOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class DEROutputStream + extends FilterOutputStream implements DERTags +{ + public DEROutputStream( + OutputStream os) + { + super(os); + } + + private void writeLength( + int length) + throws IOException + { + if (length > 127) + { + int size = 1; + int val = length; + + while ((val >>>= 8) != 0) + { + size++; + } + + write((byte)(size | 0x80)); + + for (int i = (size - 1) * 8; i >= 0; i -= 8) + { + write((byte)(length >> i)); + } + } + else + { + write((byte)length); + } + } + + void writeEncoded( + int tag, + byte[] bytes) + throws IOException + { + write(tag); + writeLength(bytes.length); + write(bytes); + } + + void writeTag(int flags, int tagNo) + throws IOException + { + if (tagNo < 31) + { + write(flags | tagNo); + } + else + { + write(flags | 0x1f); + if (tagNo < 128) + { + write(tagNo); + } + else + { + byte[] stack = new byte[5]; + int pos = stack.length; + + stack[--pos] = (byte)(tagNo & 0x7F); + + do + { + tagNo >>= 7; + stack[--pos] = (byte)(tagNo & 0x7F | 0x80); + } + while (tagNo > 127); + + write(stack, pos, stack.length - pos); + } + } + } + + void writeEncoded(int flags, int tagNo, byte[] bytes) + throws IOException + { + writeTag(flags, tagNo); + writeLength(bytes.length); + write(bytes); + } + + protected void writeNull() + throws IOException + { + write(NULL); + write(0x00); + } + + public void write(byte[] buf) + throws IOException + { + out.write(buf, 0, buf.length); + } + + public void write(byte[] buf, int offSet, int len) + throws IOException + { + out.write(buf, offSet, len); + } + + public void writeObject( + Object obj) + throws IOException + { + if (obj == null) + { + writeNull(); + } + else if (obj instanceof DERObject) + { + ((DERObject)obj).encode(this); + } + else if (obj instanceof DEREncodable) + { + ((DEREncodable)obj).getDERObject().encode(this); + } + else + { + throw new IOException("object not DEREncodable"); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERPrintableString.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERPrintableString.java new file mode 100644 index 00000000..032823dd --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERPrintableString.java @@ -0,0 +1,204 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +/** + * DER PrintableString object. + */ +public class DERPrintableString + extends ASN1Object + implements DERString +{ + String string; + + /** + * return a printable string from the passed in object. + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERPrintableString getInstance( + Object obj) + { + if (obj == null || obj instanceof DERPrintableString) + { + return (DERPrintableString)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERPrintableString(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a Printable String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERPrintableString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + /** + * basic constructor - byte encoded string. + */ + public DERPrintableString( + byte[] string) + { + char[] cs = new char[string.length]; + + for (int i = 0; i != cs.length; i++) + { + cs[i] = (char)(string[i] & 0xff); + } + + this.string = new String(cs); + } + + /** + * basic constructor - this does not validate the string + */ + public DERPrintableString( + String string) + { + this(string, false); + } + + /** + * Constructor with optional validation. + * + * @param string the base string to wrap. + * @param validate whether or not to check the string. + * @throws IllegalArgumentException if validate is true and the string + * contains characters that should not be in a PrintableString. + */ + public DERPrintableString( + String string, + boolean validate) + { + if (validate && !isPrintableString(string)) + { + throw new IllegalArgumentException("string contains illegal characters"); + } + + this.string = string; + } + + public String getString() + { + return string; + } + + public byte[] getOctets() + { + char[] cs = string.toCharArray(); + byte[] bs = new byte[cs.length]; + + for (int i = 0; i != cs.length; i++) + { + bs[i] = (byte)cs[i]; + } + + return bs; + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(PRINTABLE_STRING, this.getOctets()); + } + + public int hashCode() + { + return this.getString().hashCode(); + } + + boolean asn1Equals( + DERObject o) + { + if (!(o instanceof DERPrintableString)) + { + return false; + } + + DERPrintableString s = (DERPrintableString)o; + + return this.getString().equals(s.getString()); + } + + public String toString() + { + return string; + } + + /** + * return true if the passed in String can be represented without + * loss as a PrintableString, false otherwise. + * + * @return true if in printable set, false otherwise. + */ + public static boolean isPrintableString( + String str) + { + for (int i = str.length() - 1; i >= 0; i--) + { + char ch = str.charAt(i); + + if (ch > 0x007f) + { + return false; + } + + if ('a' <= ch && ch <= 'z') + { + continue; + } + + if ('A' <= ch && ch <= 'Z') + { + continue; + } + + if ('0' <= ch && ch <= '9') + { + continue; + } + + switch (ch) + { + case ' ': + case '\'': + case '(': + case ')': + case '+': + case '-': + case '.': + case ':': + case '=': + case '?': + case '/': + case ',': + continue; + } + + return false; + } + + return true; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERSequence.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERSequence.java new file mode 100644 index 00000000..593dfe99 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERSequence.java @@ -0,0 +1,81 @@ +package org.bouncycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Enumeration; + +@SuppressWarnings("unchecked") +public class DERSequence + extends ASN1Sequence +{ + /** + * create an empty sequence + */ + public DERSequence() + { + } + + /** + * create a sequence containing one object + */ + public DERSequence( + DEREncodable obj) + { + this.addObject(obj); + } + + /** + * create a sequence containing a vector of objects. + */ + public DERSequence( + DEREncodableVector v) + { + for (int i = 0; i != v.size(); i++) + { + this.addObject(v.get(i)); + } + } + + /** + * create a sequence containing an array of objects. + */ + public DERSequence( + ASN1Encodable[] a) + { + for (int i = 0; i != a.length; i++) + { + this.addObject(a[i]); + } + } + + /* + * A note on the implementation: + *

+ * As DER requires the constructed, definite-length model to + * be used for structured types, this varies slightly from the + * ASN.1 descriptions given. Rather than just outputing SEQUENCE, + * we also have to specify CONSTRUCTED, and the objects length. + */ + void encode( + DEROutputStream out) + throws IOException + { + // TODO Intermediate buffer could be avoided if we could calculate expected length + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(bOut); + Enumeration e = this.getObjects(); + + while (e.hasMoreElements()) + { + Object obj = e.nextElement(); + + dOut.writeObject(obj); + } + + dOut.close(); + + byte[] bytes = bOut.toByteArray(); + + out.writeEncoded(SEQUENCE | CONSTRUCTED, bytes); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERSequenceGenerator.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERSequenceGenerator.java new file mode 100644 index 00000000..458dcdc8 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERSequenceGenerator.java @@ -0,0 +1,45 @@ +package org.bouncycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class DERSequenceGenerator + extends DERGenerator +{ + private final ByteArrayOutputStream _bOut = new ByteArrayOutputStream(); + + public DERSequenceGenerator( + OutputStream out) + throws IOException + { + super(out); + } + + public DERSequenceGenerator( + OutputStream out, + int tagNo, + boolean isExplicit) + throws IOException + { + super(out, tagNo, isExplicit); + } + + public void addObject( + DEREncodable object) + throws IOException + { + object.getDERObject().encode(new DEROutputStream(_bOut)); + } + + public OutputStream getRawOutputStream() + { + return _bOut; + } + + public void close() + throws IOException + { + writeDEREncoded(DERTags.CONSTRUCTED | DERTags.SEQUENCE, _bOut.toByteArray()); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERSequenceParser.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERSequenceParser.java new file mode 100644 index 00000000..59ba7f74 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERSequenceParser.java @@ -0,0 +1,32 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +public class DERSequenceParser + implements ASN1SequenceParser +{ + private ASN1StreamParser _parser; + + DERSequenceParser(ASN1StreamParser parser) + { + this._parser = parser; + } + + public DEREncodable readObject() + throws IOException + { + return _parser.readObject(); + } + + public DERObject getDERObject() + { + try + { + return new DERSequence(_parser.readVector()); + } + catch (IOException e) + { + throw new IllegalStateException(e.getMessage()); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERSet.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERSet.java new file mode 100644 index 00000000..2634e13d --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERSet.java @@ -0,0 +1,101 @@ +package org.bouncycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Enumeration; + +/** + * A DER encoded set object + */ +@SuppressWarnings("unchecked") +public class DERSet + extends ASN1Set +{ + /** + * create an empty set + */ + public DERSet() + { + } + + /** + * @param obj - a single object that makes up the set. + */ + public DERSet( + DEREncodable obj) + { + this.addObject(obj); + } + + /** + * @param v - a vector of objects making up the set. + */ + public DERSet( + DEREncodableVector v) + { + this(v, true); + } + + /** + * create a set from an array of objects. + */ + public DERSet( + ASN1Encodable[] a) + { + for (int i = 0; i != a.length; i++) + { + this.addObject(a[i]); + } + + this.sort(); + } + + /** + * @param v - a vector of objects making up the set. + */ + DERSet( + DEREncodableVector v, + boolean needsSorting) + { + for (int i = 0; i != v.size(); i++) + { + this.addObject(v.get(i)); + } + + if (needsSorting) + { + this.sort(); + } + } + + /* + * A note on the implementation: + *

+ * As DER requires the constructed, definite-length model to + * be used for structured types, this varies slightly from the + * ASN.1 descriptions given. Rather than just outputing SET, + * we also have to specify CONSTRUCTED, and the objects length. + */ + void encode( + DEROutputStream out) + throws IOException + { + // TODO Intermediate buffer could be avoided if we could calculate expected length + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + DEROutputStream dOut = new DEROutputStream(bOut); + Enumeration e = this.getObjects(); + + while (e.hasMoreElements()) + { + Object obj = e.nextElement(); + + dOut.writeObject(obj); + } + + dOut.close(); + + byte[] bytes = bOut.toByteArray(); + + out.writeEncoded(SET | CONSTRUCTED, bytes); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERSetParser.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERSetParser.java new file mode 100644 index 00000000..2793e516 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERSetParser.java @@ -0,0 +1,32 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +public class DERSetParser + implements ASN1SetParser +{ + private ASN1StreamParser _parser; + + DERSetParser(ASN1StreamParser parser) + { + this._parser = parser; + } + + public DEREncodable readObject() + throws IOException + { + return _parser.readObject(); + } + + public DERObject getDERObject() + { + try + { + return new DERSet(_parser.readVector(), false); + } + catch (IOException e) + { + throw new ASN1ParsingException(e.getMessage(), e); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERString.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERString.java new file mode 100644 index 00000000..3143be9b --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERString.java @@ -0,0 +1,9 @@ +package org.bouncycastle.asn1; + +/** + * basic interface for DER string objects. + */ +public interface DERString +{ + public String getString(); +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERT61String.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERT61String.java new file mode 100644 index 00000000..09039fc3 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERT61String.java @@ -0,0 +1,126 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +/** + * DER T61String (also the teletex string) + */ +public class DERT61String + extends ASN1Object + implements DERString +{ + String string; + + /** + * return a T61 string from the passed in object. + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERT61String getInstance( + Object obj) + { + if (obj == null || obj instanceof DERT61String) + { + return (DERT61String)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERT61String(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an T61 String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERT61String getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + /** + * basic constructor - with bytes. + */ + public DERT61String( + byte[] string) + { + char[] cs = new char[string.length]; + + for (int i = 0; i != cs.length; i++) + { + cs[i] = (char)(string[i] & 0xff); + } + + this.string = new String(cs); + } + + /** + * basic constructor - with string. + */ + public DERT61String( + String string) + { + this.string = string; + } + + public String getString() + { + return string; + } + + public String toString() + { + return string; + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(T61_STRING, this.getOctets()); + } + + public byte[] getOctets() + { + char[] cs = string.toCharArray(); + byte[] bs = new byte[cs.length]; + + for (int i = 0; i != cs.length; i++) + { + bs[i] = (byte)cs[i]; + } + + return bs; + } + + boolean asn1Equals( + DERObject o) + { + if (!(o instanceof DERT61String)) + { + return false; + } + + return this.getString().equals(((DERT61String)o).getString()); + } + + public int hashCode() + { + return this.getString().hashCode(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERTaggedObject.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERTaggedObject.java new file mode 100644 index 00000000..a1d36878 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERTaggedObject.java @@ -0,0 +1,85 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +/** + * DER TaggedObject - in ASN.1 notation this is any object preceded by + * a [n] where n is some number - these are assumed to follow the construction + * rules (as with sequences). + */ +public class DERTaggedObject + extends ASN1TaggedObject +{ + private static final byte[] ZERO_BYTES = new byte[0]; + + /** + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + public DERTaggedObject( + int tagNo, + DEREncodable obj) + { + super(tagNo, obj); + } + + /** + * @param explicit true if an explicitly tagged object. + * @param tagNo the tag number for this object. + * @param obj the tagged object. + */ + public DERTaggedObject( + boolean explicit, + int tagNo, + DEREncodable obj) + { + super(explicit, tagNo, obj); + } + + /** + * create an implicitly tagged object that contains a zero + * length sequence. + */ + public DERTaggedObject( + int tagNo) + { + super(false, tagNo, new DERSequence()); + } + + void encode( + DEROutputStream out) + throws IOException + { + if (!empty) + { + byte[] bytes = obj.getDERObject().getEncoded(DER); + + if (explicit) + { + out.writeEncoded(CONSTRUCTED | TAGGED, tagNo, bytes); + } + else + { + // + // need to mark constructed types... + // + int flags; + if ((bytes[0] & CONSTRUCTED) != 0) + { + flags = CONSTRUCTED | TAGGED; + } + else + { + flags = TAGGED; + } + + out.writeTag(flags, tagNo); + out.write(bytes, 1, bytes.length - 1); + } + } + else + { + out.writeEncoded(CONSTRUCTED | TAGGED, tagNo, ZERO_BYTES); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERTags.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERTags.java new file mode 100644 index 00000000..ef441ef6 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERTags.java @@ -0,0 +1,36 @@ +package org.bouncycastle.asn1; + +public interface DERTags +{ + public static final int BOOLEAN = 0x01; + public static final int INTEGER = 0x02; + public static final int BIT_STRING = 0x03; + public static final int OCTET_STRING = 0x04; + public static final int NULL = 0x05; + public static final int OBJECT_IDENTIFIER = 0x06; + public static final int EXTERNAL = 0x08; + public static final int ENUMERATED = 0x0a; + public static final int SEQUENCE = 0x10; + public static final int SEQUENCE_OF = 0x10; // for completeness + public static final int SET = 0x11; + public static final int SET_OF = 0x11; // for completeness + + + public static final int NUMERIC_STRING = 0x12; + public static final int PRINTABLE_STRING = 0x13; + public static final int T61_STRING = 0x14; + public static final int VIDEOTEX_STRING = 0x15; + public static final int IA5_STRING = 0x16; + public static final int UTC_TIME = 0x17; + public static final int GENERALIZED_TIME = 0x18; + public static final int GRAPHIC_STRING = 0x19; + public static final int VISIBLE_STRING = 0x1a; + public static final int GENERAL_STRING = 0x1b; + public static final int UNIVERSAL_STRING = 0x1c; + public static final int BMP_STRING = 0x1e; + public static final int UTF8_STRING = 0x0c; + + public static final int CONSTRUCTED = 0x20; + public static final int APPLICATION = 0x40; + public static final int TAGGED = 0x80; +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERUTCTime.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERUTCTime.java new file mode 100644 index 00000000..7a056647 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERUTCTime.java @@ -0,0 +1,254 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.SimpleTimeZone; + +/** + * UTC time object. + */ +public class DERUTCTime + extends ASN1Object +{ + String time; + + /** + * return an UTC Time from the passed in object. + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERUTCTime getInstance( + Object obj) + { + if (obj == null || obj instanceof DERUTCTime) + { + return (DERUTCTime)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERUTCTime(((ASN1OctetString)obj).getOctets()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return an UTC Time from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERUTCTime getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + /** + * The correct format for this is YYMMDDHHMMSSZ (it used to be that seconds were + * never encoded. When you're creating one of these objects from scratch, that's + * what you want to use, otherwise we'll try to deal with whatever gets read from + * the input stream... (this is why the input format is different from the getTime() + * method output). + *

+ * + * @param time the time string. + */ + public DERUTCTime( + String time) + { + this.time = time; + try + { + this.getDate(); + } + catch (ParseException e) + { + throw new IllegalArgumentException("invalid date string: " + e.getMessage()); + } + } + + /** + * base constructer from a java.util.date object + */ + public DERUTCTime( + Date time) + { + SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmss'Z'"); + + dateF.setTimeZone(new SimpleTimeZone(0,"Z")); + + this.time = dateF.format(time); + } + + DERUTCTime( + byte[] bytes) + { + // + // explicitly convert to characters + // + char[] dateC = new char[bytes.length]; + + for (int i = 0; i != dateC.length; i++) + { + dateC[i] = (char)(bytes[i] & 0xff); + } + + this.time = new String(dateC); + } + + /** + * return the time as a date based on whatever a 2 digit year will return. For + * standardised processing use getAdjustedDate(). + * + * @return the resulting date + * @exception ParseException if the date string cannot be parsed. + */ + public Date getDate() + throws ParseException + { + SimpleDateFormat dateF = new SimpleDateFormat("yyMMddHHmmssz"); + + return dateF.parse(getTime()); + } + + /** + * return the time as an adjusted date + * in the range of 1950 - 2049. + * + * @return a date in the range of 1950 to 2049. + * @exception ParseException if the date string cannot be parsed. + */ + public Date getAdjustedDate() + throws ParseException + { + SimpleDateFormat dateF = new SimpleDateFormat("yyyyMMddHHmmssz"); + + dateF.setTimeZone(new SimpleTimeZone(0, "Z")); + + return dateF.parse(getAdjustedTime()); + } + + /** + * return the time - always in the form of + * YYMMDDhhmmssGMT(+hh:mm|-hh:mm). + *

+ * Normally in a certificate we would expect "Z" rather than "GMT", + * however adding the "GMT" means we can just use: + *

+     *     dateF = new SimpleDateFormat("yyMMddHHmmssz");
+     * 
+ * To read in the time and get a date which is compatible with our local + * time zone. + *

+ * Note: In some cases, due to the local date processing, this + * may lead to unexpected results. If you want to stick the normal + * convention of 1950 to 2049 use the getAdjustedTime() method. + */ + public String getTime() + { + // + // standardise the format. + // + if (time.indexOf('-') < 0 && time.indexOf('+') < 0) + { + if (time.length() == 11) + { + return time.substring(0, 10) + "00GMT+00:00"; + } + else + { + return time.substring(0, 12) + "GMT+00:00"; + } + } + else + { + int index = time.indexOf('-'); + if (index < 0) + { + index = time.indexOf('+'); + } + String d = time; + + if (index == time.length() - 3) + { + d += "00"; + } + + if (index == 10) + { + return d.substring(0, 10) + "00GMT" + d.substring(10, 13) + ":" + d.substring(13, 15); + } + else + { + return d.substring(0, 12) + "GMT" + d.substring(12, 15) + ":" + d.substring(15, 17); + } + } + } + + /** + * return a time string as an adjusted date with a 4 digit year. This goes + * in the range of 1950 - 2049. + */ + public String getAdjustedTime() + { + String d = this.getTime(); + + if (d.charAt(0) < '5') + { + return "20" + d; + } + else + { + return "19" + d; + } + } + + private byte[] getOctets() + { + char[] cs = time.toCharArray(); + byte[] bs = new byte[cs.length]; + + for (int i = 0; i != cs.length; i++) + { + bs[i] = (byte)cs[i]; + } + + return bs; + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(UTC_TIME, this.getOctets()); + } + + boolean asn1Equals( + DERObject o) + { + if (!(o instanceof DERUTCTime)) + { + return false; + } + + return time.equals(((DERUTCTime)o).time); + } + + public int hashCode() + { + return time.hashCode(); + } + + public String toString() + { + return time; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERUTF8String.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERUTF8String.java new file mode 100644 index 00000000..082aa636 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERUTF8String.java @@ -0,0 +1,109 @@ +package org.bouncycastle.asn1; + +import org.bouncycastle.util.Strings; + +import java.io.IOException; + +/** + * DER UTF8String object. + */ +public class DERUTF8String + extends ASN1Object + implements DERString +{ + String string; + + /** + * return an UTF8 string from the passed in object. + * + * @exception IllegalArgumentException + * if the object cannot be converted. + */ + public static DERUTF8String getInstance(Object obj) + { + if (obj == null || obj instanceof DERUTF8String) + { + return (DERUTF8String)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERUTF8String(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + + obj.getClass().getName()); + } + + /** + * return an UTF8 String from a tagged object. + * + * @param obj + * the tagged object holding the object we want + * @param explicit + * true if the object is meant to be explicitly tagged false + * otherwise. + * @exception IllegalArgumentException + * if the tagged object cannot be converted. + */ + public static DERUTF8String getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + /** + * basic constructor - byte encoded string. + */ + DERUTF8String(byte[] string) + { + this.string = Strings.fromUTF8ByteArray(string); + } + + /** + * basic constructor + */ + public DERUTF8String(String string) + { + this.string = string; + } + + public String getString() + { + return string; + } + + public String toString() + { + return string; + } + + public int hashCode() + { + return this.getString().hashCode(); + } + + boolean asn1Equals(DERObject o) + { + if (!(o instanceof DERUTF8String)) + { + return false; + } + + DERUTF8String s = (DERUTF8String)o; + + return this.getString().equals(s.getString()); + } + + void encode(DEROutputStream out) + throws IOException + { + out.writeEncoded(UTF8_STRING, Strings.toUTF8ByteArray(string)); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERUniversalString.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERUniversalString.java new file mode 100644 index 00000000..68be9a01 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERUniversalString.java @@ -0,0 +1,120 @@ +package org.bouncycastle.asn1; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * DER UniversalString object. + */ +public class DERUniversalString + extends ASN1Object + implements DERString +{ + private static final char[] table = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + private byte[] string; + + /** + * return a Universal String from the passed in object. + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERUniversalString getInstance( + Object obj) + { + if (obj == null || obj instanceof DERUniversalString) + { + return (DERUniversalString)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERUniversalString(((ASN1OctetString)obj).getOctets()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a Universal String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERUniversalString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + /** + * basic constructor - byte encoded string. + */ + public DERUniversalString( + byte[] string) + { + this.string = string; + } + + public String getString() + { + StringBuffer buf = new StringBuffer("#"); + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ASN1OutputStream aOut = new ASN1OutputStream(bOut); + + try + { + aOut.writeObject(this); + } + catch (IOException e) + { + throw new RuntimeException("internal error encoding BitString"); + } + + byte[] string = bOut.toByteArray(); + + for (int i = 0; i != string.length; i++) + { + buf.append(table[(string[i] >>> 4) & 0xf]); + buf.append(table[string[i] & 0xf]); + } + + return buf.toString(); + } + + public String toString() + { + return getString(); + } + + public byte[] getOctets() + { + return string; + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(UNIVERSAL_STRING, this.getOctets()); + } + + boolean asn1Equals( + DERObject o) + { + if (!(o instanceof DERUniversalString)) + { + return false; + } + + return this.getString().equals(((DERUniversalString)o).getString()); + } + + public int hashCode() + { + return this.getString().hashCode(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERUnknownTag.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERUnknownTag.java new file mode 100644 index 00000000..1feed40f --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERUnknownTag.java @@ -0,0 +1,79 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +import org.bouncycastle.util.Arrays; + +/** + * We insert one of these when we find a tag we don't recognise. + */ +public class DERUnknownTag + extends DERObject +{ + private boolean isConstructed; + private int tag; + private byte[] data; + + /** + * @param tag the tag value. + * @param data the contents octets. + */ + public DERUnknownTag( + int tag, + byte[] data) + { + this(false, tag, data); + } + + public DERUnknownTag( + boolean isConstructed, + int tag, + byte[] data) + { + this.isConstructed = isConstructed; + this.tag = tag; + this.data = data; + } + + public boolean isConstructed() + { + return isConstructed; + } + + public int getTag() + { + return tag; + } + + public byte[] getData() + { + return data; + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(isConstructed ? DERTags.CONSTRUCTED : 0, tag, data); + } + + public boolean equals( + Object o) + { + if (!(o instanceof DERUnknownTag)) + { + return false; + } + + DERUnknownTag other = (DERUnknownTag)o; + + return isConstructed == other.isConstructed + && tag == other.tag + && Arrays.areEqual(data, other.data); + } + + public int hashCode() + { + return (isConstructed ? ~0 : 0) ^ tag ^ Arrays.hashCode(data); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERVisibleString.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERVisibleString.java new file mode 100644 index 00000000..9d0c9915 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DERVisibleString.java @@ -0,0 +1,126 @@ +package org.bouncycastle.asn1; + +import java.io.IOException; + +/** + * DER VisibleString object. + */ +public class DERVisibleString + extends ASN1Object + implements DERString +{ + String string; + + /** + * return a Visible String from the passed in object. + * + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static DERVisibleString getInstance( + Object obj) + { + if (obj == null || obj instanceof DERVisibleString) + { + return (DERVisibleString)obj; + } + + if (obj instanceof ASN1OctetString) + { + return new DERVisibleString(((ASN1OctetString)obj).getOctets()); + } + + if (obj instanceof ASN1TaggedObject) + { + return getInstance(((ASN1TaggedObject)obj).getObject()); + } + + throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName()); + } + + /** + * return a Visible String from a tagged object. + * + * @param obj the tagged object holding the object we want + * @param explicit true if the object is meant to be explicitly + * tagged false otherwise. + * @exception IllegalArgumentException if the tagged object cannot + * be converted. + */ + public static DERVisibleString getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(obj.getObject()); + } + + /** + * basic constructor - byte encoded string. + */ + public DERVisibleString( + byte[] string) + { + char[] cs = new char[string.length]; + + for (int i = 0; i != cs.length; i++) + { + cs[i] = (char)(string[i] & 0xff); + } + + this.string = new String(cs); + } + + /** + * basic constructor + */ + public DERVisibleString( + String string) + { + this.string = string; + } + + public String getString() + { + return string; + } + + public String toString() + { + return string; + } + + public byte[] getOctets() + { + char[] cs = string.toCharArray(); + byte[] bs = new byte[cs.length]; + + for (int i = 0; i != cs.length; i++) + { + bs[i] = (byte)cs[i]; + } + + return bs; + } + + void encode( + DEROutputStream out) + throws IOException + { + out.writeEncoded(VISIBLE_STRING, this.getOctets()); + } + + boolean asn1Equals( + DERObject o) + { + if (!(o instanceof DERVisibleString)) + { + return false; + } + + return this.getString().equals(((DERVisibleString)o).getString()); + } + + public int hashCode() + { + return this.getString().hashCode(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DefiniteLengthInputStream.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DefiniteLengthInputStream.java new file mode 100644 index 00000000..2a7f2e73 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/DefiniteLengthInputStream.java @@ -0,0 +1,105 @@ +package org.bouncycastle.asn1; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +import org.bouncycastle.util.io.Streams; + +class DefiniteLengthInputStream + extends LimitedInputStream +{ + private static final byte[] EMPTY_BYTES = new byte[0]; + + private final int _originalLength; + private int _remaining; + + DefiniteLengthInputStream( + InputStream in, + int length) + { + super(in); + + if (length < 0) + { + throw new IllegalArgumentException("negative lengths not allowed"); + } + + this._originalLength = length; + this._remaining = length; + + if (length == 0) + { + setParentEofDetect(true); + } + } + + int getRemaining() + { + return _remaining; + } + + public int read() + throws IOException + { + if (_remaining == 0) + { + return -1; + } + + int b = _in.read(); + + if (b < 0) + { + throw new EOFException("DEF length " + _originalLength + " object truncated by " + _remaining); + } + + if (--_remaining == 0) + { + setParentEofDetect(true); + } + + return b; + } + + public int read(byte[] buf, int off, int len) + throws IOException + { + if (_remaining == 0) + { + return -1; + } + + int toRead = Math.min(len, _remaining); + int numRead = _in.read(buf, off, toRead); + + if (numRead < 0) + { + throw new EOFException("DEF length " + _originalLength + " object truncated by " + _remaining); + } + + if ((_remaining -= numRead) == 0) + { + setParentEofDetect(true); + } + + return numRead; + } + + byte[] toByteArray() + throws IOException + { + if (_remaining == 0) + { + return EMPTY_BYTES; + } + + byte[] bytes = new byte[_remaining]; + if ((_remaining -= Streams.readFully(_in, bytes)) != 0) + { + throw new EOFException("DEF length " + _originalLength + " object truncated by " + _remaining); + } + setParentEofDetect(true); + return bytes; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/IndefiniteLengthInputStream.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/IndefiniteLengthInputStream.java new file mode 100644 index 00000000..d9eac066 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/IndefiniteLengthInputStream.java @@ -0,0 +1,110 @@ +package org.bouncycastle.asn1; + +import java.io.EOFException; +import java.io.IOException; +import java.io.InputStream; + +class IndefiniteLengthInputStream + extends LimitedInputStream +{ + private int _b1; + private int _b2; + private boolean _eofReached = false; + private boolean _eofOn00 = true; + + IndefiniteLengthInputStream( + InputStream in) + throws IOException + { + super(in); + + _b1 = in.read(); + _b2 = in.read(); + + if (_b2 < 0) + { + // Corrupted stream + throw new EOFException(); + } + + checkForEof(); + } + + void setEofOn00( + boolean eofOn00) + { + _eofOn00 = eofOn00; + checkForEof(); + } + + private boolean checkForEof() + { + if (!_eofReached && _eofOn00 && (_b1 == 0x00 && _b2 == 0x00)) + { + _eofReached = true; + setParentEofDetect(true); + } + return _eofReached; + } + + public int read(byte[] b, int off, int len) + throws IOException + { + // Only use this optimisation if we aren't checking for 00 + if (_eofOn00 || len < 3) + { + return super.read(b, off, len); + } + + if (_eofReached) + { + return -1; + } + + int numRead = _in.read(b, off + 2, len - 2); + + if (numRead < 0) + { + // Corrupted stream + throw new EOFException(); + } + + b[off] = (byte)_b1; + b[off + 1] = (byte)_b2; + + _b1 = _in.read(); + _b2 = _in.read(); + + if (_b2 < 0) + { + // Corrupted stream + throw new EOFException(); + } + + return numRead + 2; + } + + public int read() + throws IOException + { + if (checkForEof()) + { + return -1; + } + + int b = _in.read(); + + if (b < 0) + { + // Corrupted stream + throw new EOFException(); + } + + int v = _b1; + + _b1 = _b2; + _b2 = b; + + return v; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/LazyDERConstructionEnumeration.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/LazyDERConstructionEnumeration.java new file mode 100644 index 00000000..379c7bf1 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/LazyDERConstructionEnumeration.java @@ -0,0 +1,44 @@ +package org.bouncycastle.asn1; + +import java.util.Enumeration; +import java.io.IOException; + +@SuppressWarnings("unchecked") +class LazyDERConstructionEnumeration + implements Enumeration +{ + private ASN1InputStream aIn; + private Object nextObj; + + public LazyDERConstructionEnumeration(byte[] encoded) + { + aIn = new ASN1InputStream(encoded, true); + nextObj = readObject(); + } + + public boolean hasMoreElements() + { + return nextObj != null; + } + + public Object nextElement() + { + Object o = nextObj; + + nextObj = readObject(); + + return o; + } + + private Object readObject() + { + try + { + return aIn.readObject(); + } + catch (IOException e) + { + throw new ASN1ParsingException("malformed DER construction: " + e, e); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/LimitedInputStream.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/LimitedInputStream.java new file mode 100644 index 00000000..5a93335d --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/LimitedInputStream.java @@ -0,0 +1,23 @@ +package org.bouncycastle.asn1; + +import java.io.InputStream; + +abstract class LimitedInputStream + extends InputStream +{ + protected final InputStream _in; + + LimitedInputStream( + InputStream in) + { + this._in = in; + } + + protected void setParentEofDetect(boolean on) + { + if (_in instanceof IndefiniteLengthInputStream) + { + ((IndefiniteLengthInputStream)_in).setEofOn00(on); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/OIDTokenizer.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/OIDTokenizer.java new file mode 100644 index 00000000..54679447 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/OIDTokenizer.java @@ -0,0 +1,48 @@ +package org.bouncycastle.asn1; + +/** + * class for breaking up an OID into it's component tokens, ala + * java.util.StringTokenizer. We need this class as some of the + * lightweight Java environment don't support classes like + * StringTokenizer. + */ +public class OIDTokenizer +{ + private String oid; + private int index; + + public OIDTokenizer( + String oid) + { + this.oid = oid; + this.index = 0; + } + + public boolean hasMoreTokens() + { + return (index != -1); + } + + public String nextToken() + { + if (index == -1) + { + return null; + } + + String token; + int end = oid.indexOf('.', index); + + if (end == -1) + { + token = oid.substring(index); + index = -1; + return token; + } + + token = oid.substring(index, end); + + index = end + 1; + return token; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/oiw/ElGamalParameter.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/oiw/ElGamalParameter.java new file mode 100644 index 00000000..21ffeeeb --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/oiw/ElGamalParameter.java @@ -0,0 +1,50 @@ +package org.bouncycastle.asn1.oiw; + +import java.math.*; +import java.util.*; + +import org.bouncycastle.asn1.*; + +@SuppressWarnings("unchecked") +public class ElGamalParameter + extends ASN1Encodable +{ + DERInteger p, g; + + public ElGamalParameter( + BigInteger p, + BigInteger g) + { + this.p = new DERInteger(p); + this.g = new DERInteger(g); + } + + public ElGamalParameter( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + p = (DERInteger)e.nextElement(); + g = (DERInteger)e.nextElement(); + } + + public BigInteger getP() + { + return p.getPositiveValue(); + } + + public BigInteger getG() + { + return g.getPositiveValue(); + } + + public DERObject toASN1Object() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(p); + v.add(g); + + return new DERSequence(v); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java new file mode 100644 index 00000000..d9690ec3 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java @@ -0,0 +1,31 @@ +package org.bouncycastle.asn1.oiw; + +import org.bouncycastle.asn1.DERObjectIdentifier; + +public interface OIWObjectIdentifiers +{ + // id-SHA1 OBJECT IDENTIFIER ::= + // {iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 } // + static final DERObjectIdentifier md4WithRSA = new DERObjectIdentifier("1.3.14.3.2.2"); + static final DERObjectIdentifier md5WithRSA = new DERObjectIdentifier("1.3.14.3.2.3"); + static final DERObjectIdentifier md4WithRSAEncryption = new DERObjectIdentifier("1.3.14.3.2.4"); + + static final DERObjectIdentifier desECB = new DERObjectIdentifier("1.3.14.3.2.6"); + static final DERObjectIdentifier desCBC = new DERObjectIdentifier("1.3.14.3.2.7"); + static final DERObjectIdentifier desOFB = new DERObjectIdentifier("1.3.14.3.2.8"); + static final DERObjectIdentifier desCFB = new DERObjectIdentifier("1.3.14.3.2.9"); + + static final DERObjectIdentifier desEDE = new DERObjectIdentifier("1.3.14.3.2.17"); + + static final DERObjectIdentifier idSHA1 = new DERObjectIdentifier("1.3.14.3.2.26"); + + static final DERObjectIdentifier dsaWithSHA1 = new DERObjectIdentifier("1.3.14.3.2.27"); + + static final DERObjectIdentifier sha1WithRSA = new DERObjectIdentifier("1.3.14.3.2.29"); + + // ElGamal Algorithm OBJECT IDENTIFIER ::= + // {iso(1) identified-organization(3) oiw(14) dirservsig(7) algorithm(2) encryption(1) 1 } + // + static final DERObjectIdentifier elGamalAlgorithm = new DERObjectIdentifier("1.3.14.7.2.1.1"); + +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/Attribute.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/Attribute.java new file mode 100644 index 00000000..0c543926 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/Attribute.java @@ -0,0 +1,82 @@ +package org.bouncycastle.asn1.pkcs; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.DERObject; +import org.bouncycastle.asn1.DERObjectIdentifier; +import org.bouncycastle.asn1.DERSequence; + +public class Attribute + extends ASN1Encodable +{ + private DERObjectIdentifier attrType; + private ASN1Set attrValues; + + /** + * return an Attribute object from the given object. + * + * @param o the object we want converted. + * @exception IllegalArgumentException if the object cannot be converted. + */ + public static Attribute getInstance( + Object o) + { + if (o == null || o instanceof Attribute) + { + return (Attribute)o; + } + + if (o instanceof ASN1Sequence) + { + return new Attribute((ASN1Sequence)o); + } + + throw new IllegalArgumentException("unknown object in factory: " + o.getClass().getName()); + } + + public Attribute( + ASN1Sequence seq) + { + attrType = (DERObjectIdentifier)seq.getObjectAt(0); + attrValues = (ASN1Set)seq.getObjectAt(1); + } + + public Attribute( + DERObjectIdentifier attrType, + ASN1Set attrValues) + { + this.attrType = attrType; + this.attrValues = attrValues; + } + + public DERObjectIdentifier getAttrType() + { + return attrType; + } + + public ASN1Set getAttrValues() + { + return attrValues; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *

+     * Attribute ::= SEQUENCE {
+     *     attrType OBJECT IDENTIFIER,
+     *     attrValues SET OF AttributeValue
+     * }
+     * 
+ */ + public DERObject toASN1Object() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(attrType); + v.add(attrValues); + + return new DERSequence(v); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/AuthenticatedSafe.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/AuthenticatedSafe.java new file mode 100644 index 00000000..fe04a5c6 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/AuthenticatedSafe.java @@ -0,0 +1,47 @@ +package org.bouncycastle.asn1.pkcs; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.BERSequence; +import org.bouncycastle.asn1.DERObject; + +public class AuthenticatedSafe + extends ASN1Encodable +{ + ContentInfo[] info; + + public AuthenticatedSafe( + ASN1Sequence seq) + { + info = new ContentInfo[seq.size()]; + + for (int i = 0; i != info.length; i++) + { + info[i] = ContentInfo.getInstance(seq.getObjectAt(i)); + } + } + + public AuthenticatedSafe( + ContentInfo[] info) + { + this.info = info; + } + + public ContentInfo[] getContentInfo() + { + return info; + } + + public DERObject toASN1Object() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + for (int i = 0; i != info.length; i++) + { + v.add(info[i]); + } + + return new BERSequence(v); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/CertBag.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/CertBag.java new file mode 100644 index 00000000..c781b4c3 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/CertBag.java @@ -0,0 +1,53 @@ +package org.bouncycastle.asn1.pkcs; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERObject; +import org.bouncycastle.asn1.DERObjectIdentifier; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.DERTaggedObject; + +public class CertBag + extends ASN1Encodable +{ + ASN1Sequence seq; + DERObjectIdentifier certId; + DERObject certValue; + + public CertBag( + ASN1Sequence seq) + { + this.seq = seq; + this.certId = (DERObjectIdentifier)seq.getObjectAt(0); + this.certValue = ((DERTaggedObject)seq.getObjectAt(1)).getObject(); + } + + public CertBag( + DERObjectIdentifier certId, + DERObject certValue) + { + this.certId = certId; + this.certValue = certValue; + } + + public DERObjectIdentifier getCertId() + { + return certId; + } + + public DERObject getCertValue() + { + return certValue; + } + + public DERObject toASN1Object() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(certId); + v.add(new DERTaggedObject(0, certValue)); + + return new DERSequence(v); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/ContentInfo.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/ContentInfo.java new file mode 100644 index 00000000..06b03ffd --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/ContentInfo.java @@ -0,0 +1,91 @@ +package org.bouncycastle.asn1.pkcs; + +import java.util.Enumeration; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.BERSequence; +import org.bouncycastle.asn1.BERTaggedObject; +import org.bouncycastle.asn1.DEREncodable; +import org.bouncycastle.asn1.DERObject; +import org.bouncycastle.asn1.DERObjectIdentifier; +import org.bouncycastle.asn1.DERTaggedObject; + +@SuppressWarnings("unchecked") +public class ContentInfo + extends ASN1Encodable + implements PKCSObjectIdentifiers +{ + private DERObjectIdentifier contentType; + private DEREncodable content; + + public static ContentInfo getInstance( + Object obj) + { + if (obj instanceof ContentInfo) + { + return (ContentInfo)obj; + } + else if (obj instanceof ASN1Sequence) + { + return new ContentInfo((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName()); + } + + public ContentInfo( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + contentType = (DERObjectIdentifier)e.nextElement(); + + if (e.hasMoreElements()) + { + content = ((DERTaggedObject)e.nextElement()).getObject(); + } + } + + public ContentInfo( + DERObjectIdentifier contentType, + DEREncodable content) + { + this.contentType = contentType; + this.content = content; + } + + public DERObjectIdentifier getContentType() + { + return contentType; + } + + public DEREncodable getContent() + { + return content; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *
+     * ContentInfo ::= SEQUENCE {
+     *          contentType ContentType,
+     *          content
+     *          [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
+     * 
+ */ + public DERObject toASN1Object() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(contentType); + + if (content != null) + { + v.add(new BERTaggedObject(0, content)); + } + + return new BERSequence(v); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/DHParameter.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/DHParameter.java new file mode 100644 index 00000000..4ab8cf70 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/DHParameter.java @@ -0,0 +1,89 @@ +package org.bouncycastle.asn1.pkcs; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERInteger; +import org.bouncycastle.asn1.DERObject; +import org.bouncycastle.asn1.DERSequence; + +@SuppressWarnings("unchecked") +public class DHParameter + extends ASN1Encodable +{ + DERInteger p, g, l; + + public DHParameter( + BigInteger p, + BigInteger g, + int l) + { + this.p = new DERInteger(p); + this.g = new DERInteger(g); + + if (l != 0) + { + this.l = new DERInteger(l); + } + else + { + this.l = null; + } + } + + public DHParameter( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + p = (DERInteger)e.nextElement(); + g = (DERInteger)e.nextElement(); + + if (e.hasMoreElements()) + { + l = (DERInteger)e.nextElement(); + } + else + { + l = null; + } + } + + public BigInteger getP() + { + return p.getPositiveValue(); + } + + public BigInteger getG() + { + return g.getPositiveValue(); + } + + public BigInteger getL() + { + if (l == null) + { + return null; + } + + return l.getPositiveValue(); + } + + public DERObject toASN1Object() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(p); + v.add(g); + + if (this.getL() != null) + { + v.add(l); + } + + return new DERSequence(v); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/PBKDF2Params.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/PBKDF2Params.java new file mode 100644 index 00000000..9fe14bd8 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/PBKDF2Params.java @@ -0,0 +1,99 @@ +package org.bouncycastle.asn1.pkcs; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERInteger; +import org.bouncycastle.asn1.DERObject; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.DERSequence; + +@SuppressWarnings("unchecked") +public class PBKDF2Params + extends ASN1Encodable +{ + ASN1OctetString octStr; + DERInteger iterationCount; + DERInteger keyLength; + + public static PBKDF2Params getInstance( + Object obj) + { + if (obj instanceof PBKDF2Params) + { + return (PBKDF2Params)obj; + } + + if (obj instanceof ASN1Sequence) + { + return new PBKDF2Params((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName()); + } + + public PBKDF2Params( + byte[] salt, + int iterationCount) + { + this.octStr = new DEROctetString(salt); + this.iterationCount = new DERInteger(iterationCount); + } + + public PBKDF2Params( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + octStr = (ASN1OctetString)e.nextElement(); + iterationCount = (DERInteger)e.nextElement(); + + if (e.hasMoreElements()) + { + keyLength = (DERInteger)e.nextElement(); + } + else + { + keyLength = null; + } + } + + public byte[] getSalt() + { + return octStr.getOctets(); + } + + public BigInteger getIterationCount() + { + return iterationCount.getValue(); + } + + public BigInteger getKeyLength() + { + if (keyLength != null) + { + return keyLength.getValue(); + } + + return null; + } + + public DERObject toASN1Object() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(octStr); + v.add(iterationCount); + + if (keyLength != null) + { + v.add(keyLength); + } + + return new DERSequence(v); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/PKCS12PBEParams.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/PKCS12PBEParams.java new file mode 100644 index 00000000..8817b35c --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/PKCS12PBEParams.java @@ -0,0 +1,69 @@ +package org.bouncycastle.asn1.pkcs; + +import java.math.BigInteger; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERInteger; +import org.bouncycastle.asn1.DERObject; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.DERSequence; + +public class PKCS12PBEParams + extends ASN1Encodable +{ + DERInteger iterations; + ASN1OctetString iv; + + public PKCS12PBEParams( + byte[] salt, + int iterations) + { + this.iv = new DEROctetString(salt); + this.iterations = new DERInteger(iterations); + } + + public PKCS12PBEParams( + ASN1Sequence seq) + { + iv = (ASN1OctetString)seq.getObjectAt(0); + iterations = (DERInteger)seq.getObjectAt(1); + } + + public static PKCS12PBEParams getInstance( + Object obj) + { + if (obj instanceof PKCS12PBEParams) + { + return (PKCS12PBEParams)obj; + } + else if (obj instanceof ASN1Sequence) + { + return new PKCS12PBEParams((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName()); + } + + public BigInteger getIterations() + { + return iterations.getValue(); + } + + public byte[] getIV() + { + return iv.getOctets(); + } + + public DERObject toASN1Object() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(iv); + v.add(iterations); + + return new DERSequence(v); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java new file mode 100644 index 00000000..19af2d67 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java @@ -0,0 +1,248 @@ +package org.bouncycastle.asn1.pkcs; + +import org.bouncycastle.asn1.DERObjectIdentifier; + +public interface PKCSObjectIdentifiers +{ + // + // pkcs-1 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 } + // + static final String pkcs_1 = "1.2.840.113549.1.1"; + static final DERObjectIdentifier rsaEncryption = new DERObjectIdentifier(pkcs_1 + ".1"); + static final DERObjectIdentifier md2WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".2"); + static final DERObjectIdentifier md4WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".3"); + static final DERObjectIdentifier md5WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".4"); + static final DERObjectIdentifier sha1WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".5"); + static final DERObjectIdentifier srsaOAEPEncryptionSET = new DERObjectIdentifier(pkcs_1 + ".6"); + static final DERObjectIdentifier id_RSAES_OAEP = new DERObjectIdentifier(pkcs_1 + ".7"); + static final DERObjectIdentifier id_mgf1 = new DERObjectIdentifier(pkcs_1 + ".8"); + static final DERObjectIdentifier id_pSpecified = new DERObjectIdentifier(pkcs_1 + ".9"); + static final DERObjectIdentifier id_RSASSA_PSS = new DERObjectIdentifier(pkcs_1 + ".10"); + static final DERObjectIdentifier sha256WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".11"); + static final DERObjectIdentifier sha384WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".12"); + static final DERObjectIdentifier sha512WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".13"); + static final DERObjectIdentifier sha224WithRSAEncryption = new DERObjectIdentifier(pkcs_1 + ".14"); + + // + // pkcs-3 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 3 } + // + static final String pkcs_3 = "1.2.840.113549.1.3"; + static final DERObjectIdentifier dhKeyAgreement = new DERObjectIdentifier(pkcs_3 + ".1"); + + // + // pkcs-5 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 5 } + // + static final String pkcs_5 = "1.2.840.113549.1.5"; + + static final DERObjectIdentifier pbeWithMD2AndDES_CBC = new DERObjectIdentifier(pkcs_5 + ".1"); + static final DERObjectIdentifier pbeWithMD2AndRC2_CBC = new DERObjectIdentifier(pkcs_5 + ".4"); + static final DERObjectIdentifier pbeWithMD5AndDES_CBC = new DERObjectIdentifier(pkcs_5 + ".3"); + static final DERObjectIdentifier pbeWithMD5AndRC2_CBC = new DERObjectIdentifier(pkcs_5 + ".6"); + static final DERObjectIdentifier pbeWithSHA1AndDES_CBC = new DERObjectIdentifier(pkcs_5 + ".10"); + static final DERObjectIdentifier pbeWithSHA1AndRC2_CBC = new DERObjectIdentifier(pkcs_5 + ".11"); + + static final DERObjectIdentifier id_PBES2 = new DERObjectIdentifier(pkcs_5 + ".13"); + + static final DERObjectIdentifier id_PBKDF2 = new DERObjectIdentifier(pkcs_5 + ".12"); + + // + // encryptionAlgorithm OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) 3 } + // + static final String encryptionAlgorithm = "1.2.840.113549.3"; + + static final DERObjectIdentifier des_EDE3_CBC = new DERObjectIdentifier(encryptionAlgorithm + ".7"); + static final DERObjectIdentifier RC2_CBC = new DERObjectIdentifier(encryptionAlgorithm + ".2"); + + // + // object identifiers for digests + // + static final String digestAlgorithm = "1.2.840.113549.2"; + // + // md2 OBJECT IDENTIFIER ::= + // {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 2} + // + static final DERObjectIdentifier md2 = new DERObjectIdentifier(digestAlgorithm + ".2"); + + // + // md4 OBJECT IDENTIFIER ::= + // {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 4} + // + static final DERObjectIdentifier md4 = new DERObjectIdentifier(digestAlgorithm + ".4"); + + // + // md5 OBJECT IDENTIFIER ::= + // {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 5} + // + static final DERObjectIdentifier md5 = new DERObjectIdentifier(digestAlgorithm + ".5"); + + static final DERObjectIdentifier id_hmacWithSHA1 = new DERObjectIdentifier(digestAlgorithm + ".7"); + static final DERObjectIdentifier id_hmacWithSHA224 = new DERObjectIdentifier(digestAlgorithm + ".8"); + static final DERObjectIdentifier id_hmacWithSHA256 = new DERObjectIdentifier(digestAlgorithm + ".9"); + static final DERObjectIdentifier id_hmacWithSHA384 = new DERObjectIdentifier(digestAlgorithm + ".10"); + static final DERObjectIdentifier id_hmacWithSHA512 = new DERObjectIdentifier(digestAlgorithm + ".11"); + + // + // pkcs-7 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 7 } + // + static final String pkcs_7 = "1.2.840.113549.1.7"; + static final DERObjectIdentifier data = new DERObjectIdentifier(pkcs_7 + ".1"); + static final DERObjectIdentifier signedData = new DERObjectIdentifier(pkcs_7 + ".2"); + static final DERObjectIdentifier envelopedData = new DERObjectIdentifier(pkcs_7 + ".3"); + static final DERObjectIdentifier signedAndEnvelopedData = new DERObjectIdentifier(pkcs_7 + ".4"); + static final DERObjectIdentifier digestedData = new DERObjectIdentifier(pkcs_7 + ".5"); + static final DERObjectIdentifier encryptedData = new DERObjectIdentifier(pkcs_7 + ".6"); + + // + // pkcs-9 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 } + // + static final String pkcs_9 = "1.2.840.113549.1.9"; + + static final DERObjectIdentifier pkcs_9_at_emailAddress = new DERObjectIdentifier(pkcs_9 + ".1"); + static final DERObjectIdentifier pkcs_9_at_unstructuredName = new DERObjectIdentifier(pkcs_9 + ".2"); + static final DERObjectIdentifier pkcs_9_at_contentType = new DERObjectIdentifier(pkcs_9 + ".3"); + static final DERObjectIdentifier pkcs_9_at_messageDigest = new DERObjectIdentifier(pkcs_9 + ".4"); + static final DERObjectIdentifier pkcs_9_at_signingTime = new DERObjectIdentifier(pkcs_9 + ".5"); + static final DERObjectIdentifier pkcs_9_at_counterSignature = new DERObjectIdentifier(pkcs_9 + ".6"); + static final DERObjectIdentifier pkcs_9_at_challengePassword = new DERObjectIdentifier(pkcs_9 + ".7"); + static final DERObjectIdentifier pkcs_9_at_unstructuredAddress = new DERObjectIdentifier(pkcs_9 + ".8"); + static final DERObjectIdentifier pkcs_9_at_extendedCertificateAttributes = new DERObjectIdentifier(pkcs_9 + ".9"); + + static final DERObjectIdentifier pkcs_9_at_signingDescription = new DERObjectIdentifier(pkcs_9 + ".13"); + static final DERObjectIdentifier pkcs_9_at_extensionRequest = new DERObjectIdentifier(pkcs_9 + ".14"); + static final DERObjectIdentifier pkcs_9_at_smimeCapabilities = new DERObjectIdentifier(pkcs_9 + ".15"); + + static final DERObjectIdentifier pkcs_9_at_friendlyName = new DERObjectIdentifier(pkcs_9 + ".20"); + static final DERObjectIdentifier pkcs_9_at_localKeyId = new DERObjectIdentifier(pkcs_9 + ".21"); + + /** @deprecated use x509Certificate instead */ + static final DERObjectIdentifier x509certType = new DERObjectIdentifier(pkcs_9 + ".22.1"); + + static final String certTypes = pkcs_9 + ".22"; + static final DERObjectIdentifier x509Certificate = new DERObjectIdentifier(certTypes + ".1"); + static final DERObjectIdentifier sdsiCertificate = new DERObjectIdentifier(certTypes + ".2"); + + static final String crlTypes = pkcs_9 + ".23"; + static final DERObjectIdentifier x509Crl = new DERObjectIdentifier(crlTypes + ".1"); + + static final DERObjectIdentifier id_alg_PWRI_KEK = new DERObjectIdentifier(pkcs_9 + ".16.3.9"); + + // + // SMIME capability sub oids. + // + static final DERObjectIdentifier preferSignedData = new DERObjectIdentifier(pkcs_9 + ".15.1"); + static final DERObjectIdentifier canNotDecryptAny = new DERObjectIdentifier(pkcs_9 + ".15.2"); + static final DERObjectIdentifier sMIMECapabilitiesVersions = new DERObjectIdentifier(pkcs_9 + ".15.3"); + + // + // id-ct OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840) + // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) ct(1)} + // + static String id_ct = "1.2.840.113549.1.9.16.1"; + + static final DERObjectIdentifier id_ct_authData = new DERObjectIdentifier(id_ct + ".2"); + static final DERObjectIdentifier id_ct_TSTInfo = new DERObjectIdentifier(id_ct + ".4"); + static final DERObjectIdentifier id_ct_compressedData = new DERObjectIdentifier(id_ct + ".9"); + static final DERObjectIdentifier id_ct_authEnvelopedData = new DERObjectIdentifier(id_ct + ".23"); + + // + // id-cti OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840) + // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) cti(6)} + // + static String id_cti = "1.2.840.113549.1.9.16.6"; + + static final DERObjectIdentifier id_cti_ets_proofOfOrigin = new DERObjectIdentifier(id_cti + ".1"); + static final DERObjectIdentifier id_cti_ets_proofOfReceipt = new DERObjectIdentifier(id_cti + ".2"); + static final DERObjectIdentifier id_cti_ets_proofOfDelivery = new DERObjectIdentifier(id_cti + ".3"); + static final DERObjectIdentifier id_cti_ets_proofOfSender = new DERObjectIdentifier(id_cti + ".4"); + static final DERObjectIdentifier id_cti_ets_proofOfApproval = new DERObjectIdentifier(id_cti + ".5"); + static final DERObjectIdentifier id_cti_ets_proofOfCreation = new DERObjectIdentifier(id_cti + ".6"); + + // + // id-aa OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840) + // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) attributes(2)} + // + static String id_aa = "1.2.840.113549.1.9.16.2"; + + static final DERObjectIdentifier id_aa_receiptRequest = new DERObjectIdentifier(id_aa + ".1"); + + static final DERObjectIdentifier id_aa_contentHint = new DERObjectIdentifier(id_aa + ".4"); // See RFC 2634 + /* + * id-aa-encrypKeyPref OBJECT IDENTIFIER ::= {id-aa 11} + * + */ + static final DERObjectIdentifier id_aa_encrypKeyPref = new DERObjectIdentifier(id_aa + ".11"); + static final DERObjectIdentifier id_aa_signingCertificate = new DERObjectIdentifier(id_aa + ".12"); + static final DERObjectIdentifier id_aa_signingCertificateV2 = new DERObjectIdentifier(id_aa + ".47"); + + static final DERObjectIdentifier id_aa_contentIdentifier = new DERObjectIdentifier(id_aa + ".7"); // See RFC 2634 + + /* + * RFC 3126 + */ + static final DERObjectIdentifier id_aa_signatureTimeStampToken = new DERObjectIdentifier(id_aa + ".14"); + + static final DERObjectIdentifier id_aa_ets_sigPolicyId = new DERObjectIdentifier(id_aa + ".15"); + static final DERObjectIdentifier id_aa_ets_commitmentType = new DERObjectIdentifier(id_aa + ".16"); + static final DERObjectIdentifier id_aa_ets_signerLocation = new DERObjectIdentifier(id_aa + ".17"); + static final DERObjectIdentifier id_aa_ets_signerAttr = new DERObjectIdentifier(id_aa + ".18"); + static final DERObjectIdentifier id_aa_ets_otherSigCert = new DERObjectIdentifier(id_aa + ".19"); + static final DERObjectIdentifier id_aa_ets_contentTimestamp = new DERObjectIdentifier(id_aa + ".20"); + static final DERObjectIdentifier id_aa_ets_certificateRefs = new DERObjectIdentifier(id_aa + ".21"); + static final DERObjectIdentifier id_aa_ets_revocationRefs = new DERObjectIdentifier(id_aa + ".22"); + static final DERObjectIdentifier id_aa_ets_certValues = new DERObjectIdentifier(id_aa + ".23"); + static final DERObjectIdentifier id_aa_ets_revocationValues = new DERObjectIdentifier(id_aa + ".24"); + static final DERObjectIdentifier id_aa_ets_escTimeStamp = new DERObjectIdentifier(id_aa + ".25"); + static final DERObjectIdentifier id_aa_ets_certCRLTimestamp = new DERObjectIdentifier(id_aa + ".26"); + static final DERObjectIdentifier id_aa_ets_archiveTimestamp = new DERObjectIdentifier(id_aa + ".27"); + + /** @deprecated use id_aa_ets_sigPolicyId instead */ + static final DERObjectIdentifier id_aa_sigPolicyId = id_aa_ets_sigPolicyId; + /** @deprecated use id_aa_ets_commitmentType instead */ + static final DERObjectIdentifier id_aa_commitmentType = id_aa_ets_commitmentType; + /** @deprecated use id_aa_ets_signerLocation instead */ + static final DERObjectIdentifier id_aa_signerLocation = id_aa_ets_signerLocation; + /** @deprecated use id_aa_ets_otherSigCert instead */ + static final DERObjectIdentifier id_aa_otherSigCert = id_aa_ets_otherSigCert; + + // + // id-spq OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840) + // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) id-spq(5)} + // + final String id_spq = "1.2.840.113549.1.9.16.5"; + + static final DERObjectIdentifier id_spq_ets_uri = new DERObjectIdentifier(id_spq + ".1"); + static final DERObjectIdentifier id_spq_ets_unotice = new DERObjectIdentifier(id_spq + ".2"); + + // + // pkcs-12 OBJECT IDENTIFIER ::= { + // iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 12 } + // + static final String pkcs_12 = "1.2.840.113549.1.12"; + static final String bagtypes = pkcs_12 + ".10.1"; + + static final DERObjectIdentifier keyBag = new DERObjectIdentifier(bagtypes + ".1"); + static final DERObjectIdentifier pkcs8ShroudedKeyBag = new DERObjectIdentifier(bagtypes + ".2"); + static final DERObjectIdentifier certBag = new DERObjectIdentifier(bagtypes + ".3"); + static final DERObjectIdentifier crlBag = new DERObjectIdentifier(bagtypes + ".4"); + static final DERObjectIdentifier secretBag = new DERObjectIdentifier(bagtypes + ".5"); + static final DERObjectIdentifier safeContentsBag = new DERObjectIdentifier(bagtypes + ".6"); + + static final String pkcs_12PbeIds = pkcs_12 + ".1"; + + static final DERObjectIdentifier pbeWithSHAAnd128BitRC4 = new DERObjectIdentifier(pkcs_12PbeIds + ".1"); + static final DERObjectIdentifier pbeWithSHAAnd40BitRC4 = new DERObjectIdentifier(pkcs_12PbeIds + ".2"); + static final DERObjectIdentifier pbeWithSHAAnd3_KeyTripleDES_CBC = new DERObjectIdentifier(pkcs_12PbeIds + ".3"); + static final DERObjectIdentifier pbeWithSHAAnd2_KeyTripleDES_CBC = new DERObjectIdentifier(pkcs_12PbeIds + ".4"); + static final DERObjectIdentifier pbeWithSHAAnd128BitRC2_CBC = new DERObjectIdentifier(pkcs_12PbeIds + ".5"); + static final DERObjectIdentifier pbewithSHAAnd40BitRC2_CBC = new DERObjectIdentifier(pkcs_12PbeIds + ".6"); + + static final DERObjectIdentifier id_alg_CMS3DESwrap = new DERObjectIdentifier("1.2.840.113549.1.9.16.3.6"); + static final DERObjectIdentifier id_alg_CMSRC2wrap = new DERObjectIdentifier("1.2.840.113549.1.9.16.3.7"); +} + diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/RC2CBCParameter.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/RC2CBCParameter.java new file mode 100644 index 00000000..23508a42 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/RC2CBCParameter.java @@ -0,0 +1,89 @@ +package org.bouncycastle.asn1.pkcs; + +import java.math.BigInteger; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERInteger; +import org.bouncycastle.asn1.DERObject; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.DERSequence; + +public class RC2CBCParameter + extends ASN1Encodable +{ + DERInteger version; + ASN1OctetString iv; + + public static RC2CBCParameter getInstance( + Object o) + { + if (o instanceof ASN1Sequence) + { + return new RC2CBCParameter((ASN1Sequence)o); + } + + throw new IllegalArgumentException("unknown object in RC2CBCParameter factory"); + } + + public RC2CBCParameter( + byte[] iv) + { + this.version = null; + this.iv = new DEROctetString(iv); + } + + public RC2CBCParameter( + int parameterVersion, + byte[] iv) + { + this.version = new DERInteger(parameterVersion); + this.iv = new DEROctetString(iv); + } + + public RC2CBCParameter( + ASN1Sequence seq) + { + if (seq.size() == 1) + { + version = null; + iv = (ASN1OctetString)seq.getObjectAt(0); + } + else + { + version = (DERInteger)seq.getObjectAt(0); + iv = (ASN1OctetString)seq.getObjectAt(1); + } + } + + public BigInteger getRC2ParameterVersion() + { + if (version == null) + { + return null; + } + + return version.getValue(); + } + + public byte[] getIV() + { + return iv.getOctets(); + } + + public DERObject toASN1Object() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + if (version != null) + { + v.add(version); + } + + v.add(iv); + + return new DERSequence(v); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/RSAPrivateKeyStructure.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/RSAPrivateKeyStructure.java new file mode 100644 index 00000000..b931ee7d --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/RSAPrivateKeyStructure.java @@ -0,0 +1,187 @@ +package org.bouncycastle.asn1.pkcs; + +import java.math.BigInteger; +import java.util.Enumeration; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1TaggedObject; +import org.bouncycastle.asn1.DERInteger; +import org.bouncycastle.asn1.DERObject; +import org.bouncycastle.asn1.DERSequence; + +@SuppressWarnings("unchecked") +public class RSAPrivateKeyStructure + extends ASN1Encodable +{ + private int version; + private BigInteger modulus; + private BigInteger publicExponent; + private BigInteger privateExponent; + private BigInteger prime1; + private BigInteger prime2; + private BigInteger exponent1; + private BigInteger exponent2; + private BigInteger coefficient; + private ASN1Sequence otherPrimeInfos = null; + + public static RSAPrivateKeyStructure getInstance( + ASN1TaggedObject obj, + boolean explicit) + { + return getInstance(ASN1Sequence.getInstance(obj, explicit)); + } + + public static RSAPrivateKeyStructure getInstance( + Object obj) + { + if (obj instanceof RSAPrivateKeyStructure) + { + return (RSAPrivateKeyStructure)obj; + } + else if (obj instanceof ASN1Sequence) + { + return new RSAPrivateKeyStructure((ASN1Sequence)obj); + } + + throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName()); + } + + public RSAPrivateKeyStructure( + BigInteger modulus, + BigInteger publicExponent, + BigInteger privateExponent, + BigInteger prime1, + BigInteger prime2, + BigInteger exponent1, + BigInteger exponent2, + BigInteger coefficient) + { + this.version = 0; + this.modulus = modulus; + this.publicExponent = publicExponent; + this.privateExponent = privateExponent; + this.prime1 = prime1; + this.prime2 = prime2; + this.exponent1 = exponent1; + this.exponent2 = exponent2; + this.coefficient = coefficient; + } + + public RSAPrivateKeyStructure( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + BigInteger v = ((DERInteger)e.nextElement()).getValue(); + if (v.intValue() != 0 && v.intValue() != 1) + { + throw new IllegalArgumentException("wrong version for RSA private key"); + } + + version = v.intValue(); + modulus = ((DERInteger)e.nextElement()).getValue(); + publicExponent = ((DERInteger)e.nextElement()).getValue(); + privateExponent = ((DERInteger)e.nextElement()).getValue(); + prime1 = ((DERInteger)e.nextElement()).getValue(); + prime2 = ((DERInteger)e.nextElement()).getValue(); + exponent1 = ((DERInteger)e.nextElement()).getValue(); + exponent2 = ((DERInteger)e.nextElement()).getValue(); + coefficient = ((DERInteger)e.nextElement()).getValue(); + + if (e.hasMoreElements()) + { + otherPrimeInfos = (ASN1Sequence)e.nextElement(); + } + } + + public int getVersion() + { + return version; + } + + public BigInteger getModulus() + { + return modulus; + } + + public BigInteger getPublicExponent() + { + return publicExponent; + } + + public BigInteger getPrivateExponent() + { + return privateExponent; + } + + public BigInteger getPrime1() + { + return prime1; + } + + public BigInteger getPrime2() + { + return prime2; + } + + public BigInteger getExponent1() + { + return exponent1; + } + + public BigInteger getExponent2() + { + return exponent2; + } + + public BigInteger getCoefficient() + { + return coefficient; + } + + /** + * This outputs the key in PKCS1v2 format. + *
+     *      RSAPrivateKey ::= SEQUENCE {
+     *                          version Version,
+     *                          modulus INTEGER, -- n
+     *                          publicExponent INTEGER, -- e
+     *                          privateExponent INTEGER, -- d
+     *                          prime1 INTEGER, -- p
+     *                          prime2 INTEGER, -- q
+     *                          exponent1 INTEGER, -- d mod (p-1)
+     *                          exponent2 INTEGER, -- d mod (q-1)
+     *                          coefficient INTEGER, -- (inverse of q) mod p
+     *                          otherPrimeInfos OtherPrimeInfos OPTIONAL
+     *                      }
+     *
+     *      Version ::= INTEGER { two-prime(0), multi(1) }
+     *        (CONSTRAINED BY {-- version must be multi if otherPrimeInfos present --})
+     * 
+ *

+ * This routine is written to output PKCS1 version 2.1, private keys. + */ + public DERObject toASN1Object() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(new DERInteger(version)); // version + v.add(new DERInteger(getModulus())); + v.add(new DERInteger(getPublicExponent())); + v.add(new DERInteger(getPrivateExponent())); + v.add(new DERInteger(getPrime1())); + v.add(new DERInteger(getPrime2())); + v.add(new DERInteger(getExponent1())); + v.add(new DERInteger(getExponent2())); + v.add(new DERInteger(getCoefficient())); + + if (otherPrimeInfos != null) + { + v.add(otherPrimeInfos); + } + + return new DERSequence(v); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/SafeBag.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/SafeBag.java new file mode 100644 index 00000000..2808d92d --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/SafeBag.java @@ -0,0 +1,78 @@ +package org.bouncycastle.asn1.pkcs; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.DERObject; +import org.bouncycastle.asn1.DERObjectIdentifier; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.DERTaggedObject; + +public class SafeBag + extends ASN1Encodable +{ + DERObjectIdentifier bagId; + DERObject bagValue; + ASN1Set bagAttributes; + + public SafeBag( + DERObjectIdentifier oid, + DERObject obj) + { + this.bagId = oid; + this.bagValue = obj; + this.bagAttributes = null; + } + + public SafeBag( + DERObjectIdentifier oid, + DERObject obj, + ASN1Set bagAttributes) + { + this.bagId = oid; + this.bagValue = obj; + this.bagAttributes = bagAttributes; + } + + public SafeBag( + ASN1Sequence seq) + { + this.bagId = (DERObjectIdentifier)seq.getObjectAt(0); + this.bagValue = ((DERTaggedObject)seq.getObjectAt(1)).getObject(); + if (seq.size() == 3) + { + this.bagAttributes = (ASN1Set)seq.getObjectAt(2); + } + } + + public DERObjectIdentifier getBagId() + { + return bagId; + } + + public DERObject getBagValue() + { + return bagValue; + } + + public ASN1Set getBagAttributes() + { + return bagAttributes; + } + + public DERObject toASN1Object() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(bagId); + v.add(new DERTaggedObject(0, bagValue)); + + if (bagAttributes != null) + { + v.add(bagAttributes); + } + + return new DERSequence(v); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/SignedData.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/SignedData.java new file mode 100644 index 00000000..1ac0c05e --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/pkcs/SignedData.java @@ -0,0 +1,167 @@ +package org.bouncycastle.asn1.pkcs; + +import java.util.Enumeration; + +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1EncodableVector; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.BERSequence; +import org.bouncycastle.asn1.DERInteger; +import org.bouncycastle.asn1.DERObject; +import org.bouncycastle.asn1.DERTaggedObject; + +/** + * a PKCS#7 signed data object. + */ +@SuppressWarnings("unchecked") +public class SignedData + extends ASN1Encodable + implements PKCSObjectIdentifiers +{ + private DERInteger version; + private ASN1Set digestAlgorithms; + private ContentInfo contentInfo; + private ASN1Set certificates; + private ASN1Set crls; + private ASN1Set signerInfos; + + public static SignedData getInstance( + Object o) + { + if (o instanceof SignedData) + { + return (SignedData)o; + } + else if (o instanceof ASN1Sequence) + { + return new SignedData((ASN1Sequence)o); + } + + throw new IllegalArgumentException("unknown object in factory: " + o); + } + + public SignedData( + DERInteger _version, + ASN1Set _digestAlgorithms, + ContentInfo _contentInfo, + ASN1Set _certificates, + ASN1Set _crls, + ASN1Set _signerInfos) + { + version = _version; + digestAlgorithms = _digestAlgorithms; + contentInfo = _contentInfo; + certificates = _certificates; + crls = _crls; + signerInfos = _signerInfos; + } + + public SignedData( + ASN1Sequence seq) + { + Enumeration e = seq.getObjects(); + + version = (DERInteger)e.nextElement(); + digestAlgorithms = ((ASN1Set)e.nextElement()); + contentInfo = ContentInfo.getInstance(e.nextElement()); + + while (e.hasMoreElements()) + { + DERObject o = (DERObject)e.nextElement(); + + // + // an interesting feature of SignedData is that there appear to be varying implementations... + // for the moment we ignore anything which doesn't fit. + // + if (o instanceof DERTaggedObject) + { + DERTaggedObject tagged = (DERTaggedObject)o; + + switch (tagged.getTagNo()) + { + case 0: + certificates = ASN1Set.getInstance(tagged, false); + break; + case 1: + crls = ASN1Set.getInstance(tagged, false); + break; + default: + throw new IllegalArgumentException("unknown tag value " + tagged.getTagNo()); + } + } + else + { + signerInfos = (ASN1Set)o; + } + } + } + + public DERInteger getVersion() + { + return version; + } + + public ASN1Set getDigestAlgorithms() + { + return digestAlgorithms; + } + + public ContentInfo getContentInfo() + { + return contentInfo; + } + + public ASN1Set getCertificates() + { + return certificates; + } + + public ASN1Set getCRLs() + { + return crls; + } + + public ASN1Set getSignerInfos() + { + return signerInfos; + } + + /** + * Produce an object suitable for an ASN1OutputStream. + *

+     *  SignedData ::= SEQUENCE {
+     *      version Version,
+     *      digestAlgorithms DigestAlgorithmIdentifiers,
+     *      contentInfo ContentInfo,
+     *      certificates
+     *          [0] IMPLICIT ExtendedCertificatesAndCertificates
+     *                   OPTIONAL,
+     *      crls
+     *          [1] IMPLICIT CertificateRevocationLists OPTIONAL,
+     *      signerInfos SignerInfos }
+     * 
+ */ + public DERObject toASN1Object() + { + ASN1EncodableVector v = new ASN1EncodableVector(); + + v.add(version); + v.add(digestAlgorithms); + v.add(contentInfo); + + if (certificates != null) + { + v.add(new DERTaggedObject(false, 0, certificates)); + } + + if (crls != null) + { + v.add(new DERTaggedObject(false, 1, crls)); + } + + v.add(signerInfos); + + return new BERSequence(v); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/util/ASN1Dump.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/util/ASN1Dump.java new file mode 100644 index 00000000..250fac1d --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/util/ASN1Dump.java @@ -0,0 +1,473 @@ +package org.bouncycastle.asn1.util; + +import java.io.IOException; +import java.util.Enumeration; + +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.BERApplicationSpecific; +import org.bouncycastle.asn1.BERConstructedOctetString; +import org.bouncycastle.asn1.BERConstructedSequence; +import org.bouncycastle.asn1.BERSequence; +import org.bouncycastle.asn1.BERSet; +import org.bouncycastle.asn1.BERTaggedObject; +import org.bouncycastle.asn1.DERApplicationSpecific; +import org.bouncycastle.asn1.DERBMPString; +import org.bouncycastle.asn1.DERBitString; +import org.bouncycastle.asn1.DERBoolean; +import org.bouncycastle.asn1.DERConstructedSequence; +import org.bouncycastle.asn1.DERConstructedSet; +import org.bouncycastle.asn1.DEREncodable; +import org.bouncycastle.asn1.DEREnumerated; +import org.bouncycastle.asn1.DERExternal; +import org.bouncycastle.asn1.DERGeneralizedTime; +import org.bouncycastle.asn1.DERIA5String; +import org.bouncycastle.asn1.DERInteger; +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.DERObject; +import org.bouncycastle.asn1.DERObjectIdentifier; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.DERPrintableString; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.DERSet; +import org.bouncycastle.asn1.DERT61String; +import org.bouncycastle.asn1.DERTaggedObject; +import org.bouncycastle.asn1.DERTags; +import org.bouncycastle.asn1.DERUTCTime; +import org.bouncycastle.asn1.DERUTF8String; +import org.bouncycastle.asn1.DERUnknownTag; +import org.bouncycastle.asn1.DERVisibleString; +import org.bouncycastle.util.encoders.Hex; + +@SuppressWarnings({ "unchecked", "deprecation" }) +public class ASN1Dump +{ + private static final String TAB = " "; + private static final int SAMPLE_SIZE = 32; + + /** + * dump a DER object as a formatted string with indentation + * + * @param obj the DERObject to be dumped out. + */ + static void _dumpAsString( + String indent, + boolean verbose, + DERObject obj, + StringBuffer buf) + { + String nl = System.getProperty("line.separator"); + if (obj instanceof ASN1Sequence) + { + Enumeration e = ((ASN1Sequence)obj).getObjects(); + String tab = indent + TAB; + + buf.append(indent); + if (obj instanceof BERConstructedSequence) + { + buf.append("BER ConstructedSequence"); + } + else if (obj instanceof DERConstructedSequence) + { + buf.append("DER ConstructedSequence"); + } + else if (obj instanceof BERSequence) + { + buf.append("BER Sequence"); + } + else if (obj instanceof DERSequence) + { + buf.append("DER Sequence"); + } + else + { + buf.append("Sequence"); + } + + buf.append(nl); + + while (e.hasMoreElements()) + { + Object o = e.nextElement(); + + if (o == null || o.equals(new DERNull())) + { + buf.append(tab); + buf.append("NULL"); + buf.append(nl); + } + else if (o instanceof DERObject) + { + _dumpAsString(tab, verbose, (DERObject)o, buf); + } + else + { + _dumpAsString(tab, verbose, ((DEREncodable)o).getDERObject(), buf); + } + } + } + else if (obj instanceof DERTaggedObject) + { + String tab = indent + TAB; + + buf.append(indent); + if (obj instanceof BERTaggedObject) + { + buf.append("BER Tagged ["); + } + else + { + buf.append("Tagged ["); + } + + DERTaggedObject o = (DERTaggedObject)obj; + + buf.append(Integer.toString(o.getTagNo())); + buf.append(']'); + + if (!o.isExplicit()) + { + buf.append(" IMPLICIT "); + } + + buf.append(nl); + + if (o.isEmpty()) + { + buf.append(tab); + buf.append("EMPTY"); + buf.append(nl); + } + else + { + _dumpAsString(tab, verbose, o.getObject(), buf); + } + } + else if (obj instanceof DERConstructedSet) + { + Enumeration e = ((ASN1Set)obj).getObjects(); + String tab = indent + TAB; + + buf.append(indent); + buf.append("ConstructedSet"); + buf.append(nl); + + while (e.hasMoreElements()) + { + Object o = e.nextElement(); + + if (o == null) + { + buf.append(tab); + buf.append("NULL"); + buf.append(nl); + } + else if (o instanceof DERObject) + { + _dumpAsString(tab, verbose, (DERObject)o, buf); + } + else + { + _dumpAsString(tab, verbose, ((DEREncodable)o).getDERObject(), buf); + } + } + } + else if (obj instanceof BERSet) + { + Enumeration e = ((ASN1Set)obj).getObjects(); + String tab = indent + TAB; + + buf.append(indent); + buf.append("BER Set"); + buf.append(nl); + + while (e.hasMoreElements()) + { + Object o = e.nextElement(); + + if (o == null) + { + buf.append(tab); + buf.append("NULL"); + buf.append(nl); + } + else if (o instanceof DERObject) + { + _dumpAsString(tab, verbose, (DERObject)o, buf); + } + else + { + _dumpAsString(tab, verbose, ((DEREncodable)o).getDERObject(), buf); + } + } + } + else if (obj instanceof DERSet) + { + Enumeration e = ((ASN1Set)obj).getObjects(); + String tab = indent + TAB; + + buf.append(indent); + buf.append("DER Set"); + buf.append(nl); + + while (e.hasMoreElements()) + { + Object o = e.nextElement(); + + if (o == null) + { + buf.append(tab); + buf.append("NULL"); + buf.append(nl); + } + else if (o instanceof DERObject) + { + _dumpAsString(tab, verbose, (DERObject)o, buf); + } + else + { + _dumpAsString(tab, verbose, ((DEREncodable)o).getDERObject(), buf); + } + } + } + else if (obj instanceof DERObjectIdentifier) + { + buf.append(indent + "ObjectIdentifier(" + ((DERObjectIdentifier)obj).getId() + ")" + nl); + } + else if (obj instanceof DERBoolean) + { + buf.append(indent + "Boolean(" + ((DERBoolean)obj).isTrue() + ")" + nl); + } + else if (obj instanceof DERInteger) + { + buf.append(indent + "Integer(" + ((DERInteger)obj).getValue() + ")" + nl); + } + else if (obj instanceof BERConstructedOctetString) + { + ASN1OctetString oct = (ASN1OctetString)obj; + buf.append(indent + "BER Constructed Octet String" + "[" + oct.getOctets().length + "] "); + if (verbose) + { + buf.append(dumpBinaryDataAsString(indent, oct.getOctets())); + } + else{ + buf.append(nl); + } + } + else if (obj instanceof DEROctetString) + { + ASN1OctetString oct = (ASN1OctetString)obj; + buf.append(indent + "DER Octet String" + "[" + oct.getOctets().length + "] "); + if (verbose) + { + buf.append(dumpBinaryDataAsString(indent, oct.getOctets())); + } + else{ + buf.append(nl); + } + } + else if (obj instanceof DERBitString) + { + DERBitString bt = (DERBitString)obj; + buf.append(indent + "DER Bit String" + "[" + bt.getBytes().length + ", " + bt.getPadBits() + "] "); + if (verbose) + { + buf.append(dumpBinaryDataAsString(indent, bt.getBytes())); + } + else{ + buf.append(nl); + } + } + else if (obj instanceof DERIA5String) + { + buf.append(indent + "IA5String(" + ((DERIA5String)obj).getString() + ") " + nl); + } + else if (obj instanceof DERUTF8String) + { + buf.append(indent + "UTF8String(" + ((DERUTF8String)obj).getString() + ") " + nl); + } + else if (obj instanceof DERPrintableString) + { + buf.append(indent + "PrintableString(" + ((DERPrintableString)obj).getString() + ") " + nl); + } + else if (obj instanceof DERVisibleString) + { + buf.append(indent + "VisibleString(" + ((DERVisibleString)obj).getString() + ") " + nl); + } + else if (obj instanceof DERBMPString) + { + buf.append(indent + "BMPString(" + ((DERBMPString)obj).getString() + ") " + nl); + } + else if (obj instanceof DERT61String) + { + buf.append(indent + "T61String(" + ((DERT61String)obj).getString() + ") " + nl); + } + else if (obj instanceof DERUTCTime) + { + buf.append(indent + "UTCTime(" + ((DERUTCTime)obj).getTime() + ") " + nl); + } + else if (obj instanceof DERGeneralizedTime) + { + buf.append(indent + "GeneralizedTime(" + ((DERGeneralizedTime)obj).getTime() + ") " + nl); + } + else if (obj instanceof DERUnknownTag) + { + buf.append(indent + "Unknown " + Integer.toString(((DERUnknownTag)obj).getTag(), 16) + " " + new String(Hex.encode(((DERUnknownTag)obj).getData())) + nl); + } + else if (obj instanceof BERApplicationSpecific) + { + buf.append(outputApplicationSpecific("BER", indent, verbose, obj, nl)); + } + else if (obj instanceof DERApplicationSpecific) + { + buf.append(outputApplicationSpecific("DER", indent, verbose, obj, nl)); + } + else if (obj instanceof DEREnumerated) + { + DEREnumerated en = (DEREnumerated) obj; + buf.append(indent + "DER Enumerated(" + en.getValue() + ")" + nl); + } + else if (obj instanceof DERExternal) + { + DERExternal ext = (DERExternal) obj; + buf.append(indent + "External " + nl); + String tab = indent + TAB; + if (ext.getDirectReference() != null) + { + buf.append(tab + "Direct Reference: " + ext.getDirectReference().getId() + nl); + } + if (ext.getIndirectReference() != null) + { + buf.append(tab + "Indirect Reference: " + ext.getIndirectReference().toString() + nl); + } + if (ext.getDataValueDescriptor() != null) + { + _dumpAsString(tab, verbose, ext.getDataValueDescriptor(), buf); + } + buf.append(tab + "Encoding: " + ext.getEncoding() + nl); + _dumpAsString(tab, verbose, ext.getExternalContent(), buf); + } + else + { + buf.append(indent + obj.toString() + nl); + } + } + + private static String outputApplicationSpecific(String type, String indent, boolean verbose, DERObject obj, String nl) + { + DERApplicationSpecific app = (DERApplicationSpecific)obj; + StringBuffer buf = new StringBuffer(); + + if (app.isConstructed()) + { + try + { + ASN1Sequence s = ASN1Sequence.getInstance(app.getObject(DERTags.SEQUENCE)); + buf.append(indent + type + " ApplicationSpecific[" + app.getApplicationTag() + "]" + nl); + for (Enumeration e = s.getObjects(); e.hasMoreElements();) + { + _dumpAsString(indent + TAB, verbose, (DERObject)e.nextElement(), buf); + } + } + catch (IOException e) + { + buf.append(e); + } + return buf.toString(); + } + + return indent + type + " ApplicationSpecific[" + app.getApplicationTag() + "] (" + new String(Hex.encode(app.getContents())) + ")" + nl; + } + + /** + * dump out a DER object as a formatted string, in non-verbose mode. + * + * @param obj the DERObject to be dumped out. + * @return the resulting string. + */ + public static String dumpAsString( + Object obj) + { + return dumpAsString(obj, false); + } + + /** + * Dump out the object as a string. + * + * @param obj the object to be dumped + * @param verbose if true, dump out the contents of octet and bit strings. + * @return the resulting string. + */ + public static String dumpAsString( + Object obj, + boolean verbose) + { + StringBuffer buf = new StringBuffer(); + + if (obj instanceof DERObject) + { + _dumpAsString("", verbose, (DERObject)obj, buf); + } + else if (obj instanceof DEREncodable) + { + _dumpAsString("", verbose, ((DEREncodable)obj).getDERObject(), buf); + } + else + { + return "unknown object type " + obj.toString(); + } + + return buf.toString(); + } + + private static String dumpBinaryDataAsString(String indent, byte[] bytes) + { + String nl = System.getProperty("line.separator"); + StringBuffer buf = new StringBuffer(); + + indent += TAB; + + buf.append(nl); + for (int i = 0; i < bytes.length; i += SAMPLE_SIZE) + { + if (bytes.length - i > SAMPLE_SIZE) + { + buf.append(indent); + buf.append(new String(Hex.encode(bytes, i, SAMPLE_SIZE))); + buf.append(TAB); + buf.append(calculateAscString(bytes, i, SAMPLE_SIZE)); + buf.append(nl); + } + else + { + buf.append(indent); + buf.append(new String(Hex.encode(bytes, i, bytes.length - i))); + for (int j = bytes.length - i; j != SAMPLE_SIZE; j++) + { + buf.append(" "); + } + buf.append(TAB); + buf.append(calculateAscString(bytes, i, bytes.length - i)); + buf.append(nl); + } + } + + return buf.toString(); + } + + private static String calculateAscString(byte[] bytes, int off, int len) + { + StringBuffer buf = new StringBuffer(); + + for (int i = off; i != off + len; i++) + { + if (bytes[i] >= ' ' && bytes[i] <= '~') + { + buf.append((char)bytes[i]); + } + } + + return buf.toString(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/util/ASN1Dump.java.orig b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/util/ASN1Dump.java.orig new file mode 100644 index 00000000..68c65cbe --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/util/ASN1Dump.java.orig @@ -0,0 +1,440 @@ +package org.bouncycastle.asn1.util; + +import org.bouncycastle.asn1.ASN1OctetString; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.ASN1Set; +import org.bouncycastle.asn1.BERConstructedOctetString; +import org.bouncycastle.asn1.BERConstructedSequence; +import org.bouncycastle.asn1.BERSequence; +import org.bouncycastle.asn1.BERSet; +import org.bouncycastle.asn1.BERTaggedObject; +import org.bouncycastle.asn1.DERBMPString; +import org.bouncycastle.asn1.DERBitString; +import org.bouncycastle.asn1.DERBoolean; +import org.bouncycastle.asn1.DERConstructedSequence; +import org.bouncycastle.asn1.DERConstructedSet; +import org.bouncycastle.asn1.DEREncodable; +import org.bouncycastle.asn1.DERGeneralizedTime; +import org.bouncycastle.asn1.DERIA5String; +import org.bouncycastle.asn1.DERInteger; +import org.bouncycastle.asn1.DERNull; +import org.bouncycastle.asn1.DERObject; +import org.bouncycastle.asn1.DERObjectIdentifier; +import org.bouncycastle.asn1.DEROctetString; +import org.bouncycastle.asn1.DERPrintableString; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.asn1.DERSet; +import org.bouncycastle.asn1.DERT61String; +import org.bouncycastle.asn1.DERTaggedObject; +import org.bouncycastle.asn1.DERUTCTime; +import org.bouncycastle.asn1.DERUTF8String; +import org.bouncycastle.asn1.DERUnknownTag; +import org.bouncycastle.asn1.DERVisibleString; +import org.bouncycastle.asn1.DERApplicationSpecific; +import org.bouncycastle.asn1.DERTags; +import org.bouncycastle.asn1.BERApplicationSpecific; +import org.bouncycastle.util.encoders.Hex; + +import java.util.Enumeration; +import java.io.IOException; + +public class ASN1Dump +{ + private static final String TAB = " "; + private static final int SAMPLE_SIZE = 32; + + /** + * dump a DER object as a formatted string with indentation + * + * @param obj the DERObject to be dumped out. + */ + static String _dumpAsString( + String indent, + boolean verbose, + DERObject obj) + { + String nl = System.getProperty("line.separator"); + if (obj instanceof ASN1Sequence) + { + StringBuffer buf = new StringBuffer(); + Enumeration e = ((ASN1Sequence)obj).getObjects(); + String tab = indent + TAB; + + buf.append(indent); + if (obj instanceof BERConstructedSequence) + { + buf.append("BER ConstructedSequence"); + } + else if (obj instanceof DERConstructedSequence) + { + buf.append("DER ConstructedSequence"); + } + else if (obj instanceof BERSequence) + { + buf.append("BER Sequence"); + } + else if (obj instanceof DERSequence) + { + buf.append("DER Sequence"); + } + else + { + buf.append("Sequence"); + } + + buf.append(nl); + + while (e.hasMoreElements()) + { + Object o = e.nextElement(); + + if (o == null || o.equals(new DERNull())) + { + buf.append(tab); + buf.append("NULL"); + buf.append(nl); + } + else if (o instanceof DERObject) + { + buf.append(_dumpAsString(tab, verbose, (DERObject)o)); + } + else + { + buf.append(_dumpAsString(tab, verbose, ((DEREncodable)o).getDERObject())); + } + } + return buf.toString(); + } + else if (obj instanceof DERTaggedObject) + { + StringBuffer buf = new StringBuffer(); + String tab = indent + TAB; + + buf.append(indent); + if (obj instanceof BERTaggedObject) + { + buf.append("BER Tagged ["); + } + else + { + buf.append("Tagged ["); + } + + DERTaggedObject o = (DERTaggedObject)obj; + + buf.append(Integer.toString(o.getTagNo())); + buf.append(']'); + + if (!o.isExplicit()) + { + buf.append(" IMPLICIT "); + } + + buf.append(nl); + + if (o.isEmpty()) + { + buf.append(tab); + buf.append("EMPTY"); + buf.append(nl); + } + else + { + buf.append(_dumpAsString(tab, verbose, o.getObject())); + } + + return buf.toString(); + } + else if (obj instanceof DERConstructedSet) + { + StringBuffer buf = new StringBuffer(); + Enumeration e = ((ASN1Set)obj).getObjects(); + String tab = indent + TAB; + + buf.append(indent); + buf.append("ConstructedSet"); + buf.append(nl); + + while (e.hasMoreElements()) + { + Object o = e.nextElement(); + + if (o == null) + { + buf.append(tab); + buf.append("NULL"); + buf.append(nl); + } + else if (o instanceof DERObject) + { + buf.append(_dumpAsString(tab, verbose, (DERObject)o)); + } + else + { + buf.append(_dumpAsString(tab, verbose, ((DEREncodable)o).getDERObject())); + } + } + return buf.toString(); + } + else if (obj instanceof BERSet) + { + StringBuffer buf = new StringBuffer(); + Enumeration e = ((ASN1Set)obj).getObjects(); + String tab = indent + TAB; + + buf.append(indent); + buf.append("BER Set"); + buf.append(nl); + + while (e.hasMoreElements()) + { + Object o = e.nextElement(); + + if (o == null) + { + buf.append(tab); + buf.append("NULL"); + buf.append(nl); + } + else if (o instanceof DERObject) + { + buf.append(_dumpAsString(tab, verbose, (DERObject)o)); + } + else + { + buf.append(_dumpAsString(tab, verbose, ((DEREncodable)o).getDERObject())); + } + } + return buf.toString(); + } + else if (obj instanceof DERSet) + { + StringBuffer buf = new StringBuffer(); + Enumeration e = ((ASN1Set)obj).getObjects(); + String tab = indent + TAB; + + buf.append(indent); + buf.append("DER Set"); + buf.append(nl); + + while (e.hasMoreElements()) + { + Object o = e.nextElement(); + + if (o == null) + { + buf.append(tab); + buf.append("NULL"); + buf.append(nl); + } + else if (o instanceof DERObject) + { + buf.append(_dumpAsString(tab, verbose, (DERObject)o)); + } + else + { + buf.append(_dumpAsString(tab, verbose, ((DEREncodable)o).getDERObject())); + } + } + return buf.toString(); + } + else if (obj instanceof DERObjectIdentifier) + { + return indent + "ObjectIdentifier(" + ((DERObjectIdentifier)obj).getId() + ")" + nl; + } + else if (obj instanceof DERBoolean) + { + return indent + "Boolean(" + ((DERBoolean)obj).isTrue() + ")" + nl; + } + else if (obj instanceof DERInteger) + { + return indent + "Integer(" + ((DERInteger)obj).getValue() + ")" + nl; + } + else if (obj instanceof BERConstructedOctetString) + { + ASN1OctetString oct = (ASN1OctetString)obj; + if (verbose) + { + return indent + "BER Constructed Octet String" + "[" + oct.getOctets().length + "] " + dumpBinaryDataAsString(indent, oct.getOctets()) + nl; + } + return indent + "BER Constructed Octet String" + "[" + oct.getOctets().length + "] " + nl; + } + else if (obj instanceof DEROctetString) + { + ASN1OctetString oct = (ASN1OctetString)obj; + if (verbose) + { + return indent + "DER Octet String" + "[" + oct.getOctets().length + "] " + dumpBinaryDataAsString(indent, oct.getOctets()) + nl; + } + return indent + "DER Octet String" + "[" + oct.getOctets().length + "] " + nl; + } + else if (obj instanceof DERBitString) + { + DERBitString bt = (DERBitString)obj; + if (verbose) + { + return indent + "DER Bit String" + "[" + bt.getBytes().length + ", " + bt.getPadBits() + "] " + dumpBinaryDataAsString(indent, bt.getBytes()) + nl; + } + return indent + "DER Bit String" + "[" + bt.getBytes().length + ", " + bt.getPadBits() + "] " + nl; + } + else if (obj instanceof DERIA5String) + { + return indent + "IA5String(" + ((DERIA5String)obj).getString() + ") " + nl; + } + else if (obj instanceof DERUTF8String) + { + return indent + "UTF8String(" + ((DERUTF8String)obj).getString() + ") " + nl; + } + else if (obj instanceof DERPrintableString) + { + return indent + "PrintableString(" + ((DERPrintableString)obj).getString() + ") " + nl; + } + else if (obj instanceof DERVisibleString) + { + return indent + "VisibleString(" + ((DERVisibleString)obj).getString() + ") " + nl; + } + else if (obj instanceof DERBMPString) + { + return indent + "BMPString(" + ((DERBMPString)obj).getString() + ") " + nl; + } + else if (obj instanceof DERT61String) + { + return indent + "T61String(" + ((DERT61String)obj).getString() + ") " + nl; + } + else if (obj instanceof DERUTCTime) + { + return indent + "UTCTime(" + ((DERUTCTime)obj).getTime() + ") " + nl; + } + else if (obj instanceof DERGeneralizedTime) + { + return indent + "GeneralizedTime(" + ((DERGeneralizedTime)obj).getTime() + ") " + nl; + } + else if (obj instanceof DERUnknownTag) + { + return indent + "Unknown " + Integer.toString(((DERUnknownTag)obj).getTag(), 16) + " " + new String(Hex.encode(((DERUnknownTag)obj).getData())) + nl; + } + else if (obj instanceof BERApplicationSpecific) + { + return outputApplicationSpecific("BER", indent, verbose, obj, nl); + } + else if (obj instanceof DERApplicationSpecific) + { + return outputApplicationSpecific("DER", indent, verbose, obj, nl); + } + else + { + return indent + obj.toString() + nl; + } + } + + private static String outputApplicationSpecific(String type, String indent, boolean verbose, DERObject obj, String nl) + { + DERApplicationSpecific app = (DERApplicationSpecific)obj; + StringBuffer buf = new StringBuffer(); + + if (app.isConstructed()) + { + try + { + ASN1Sequence s = ASN1Sequence.getInstance(app.getObject(DERTags.SEQUENCE)); + buf.append(indent + type + " ApplicationSpecific[" + app.getApplicationTag() + "]" + nl); + for (Enumeration e = s.getObjects(); e.hasMoreElements();) + { + buf.append(_dumpAsString(indent + TAB, verbose, (DERObject)e.nextElement())); + } + } + catch (IOException e) + { + buf.append(e); + } + return buf.toString(); + } + + return indent + type + " ApplicationSpecific[" + app.getApplicationTag() + "] (" + new String(Hex.encode(app.getContents())) + ")" + nl; + } + + /** + * dump out a DER object as a formatted string, in non-verbose mode. + * + * @param obj the DERObject to be dumped out. + * @return the resulting string. + */ + public static String dumpAsString( + Object obj) + { + return dumpAsString(obj, false); + } + + /** + * Dump out the object as a string. + * + * @param obj the object to be dumped + * @param verbose if true, dump out the contents of octet and bit strings. + * @return the resulting string. + */ + public static String dumpAsString( + Object obj, + boolean verbose) + { + if (obj instanceof DERObject) + { + return _dumpAsString("", verbose, (DERObject)obj); + } + else if (obj instanceof DEREncodable) + { + return _dumpAsString("", verbose, ((DEREncodable)obj).getDERObject()); + } + + return "unknown object type " + obj.toString(); + } + + private static String dumpBinaryDataAsString(String indent, byte[] bytes) + { + String nl = System.getProperty("line.separator"); + StringBuffer buf = new StringBuffer(); + + indent += TAB; + + buf.append(nl); + for (int i = 0; i < bytes.length; i += SAMPLE_SIZE) + { + if (bytes.length - i > SAMPLE_SIZE) + { + buf.append(indent); + buf.append(new String(Hex.encode(bytes, i, SAMPLE_SIZE))); + buf.append(TAB); + buf.append(calculateAscString(bytes, i, SAMPLE_SIZE)); + buf.append(nl); + } + else + { + buf.append(indent); + buf.append(new String(Hex.encode(bytes, i, bytes.length - i))); + for (int j = bytes.length - i; j != SAMPLE_SIZE; j++) + { + buf.append(" "); + } + buf.append(TAB); + buf.append(calculateAscString(bytes, i, bytes.length - i)); + buf.append(nl); + } + } + + return buf.toString(); + } + + private static String calculateAscString(byte[] bytes, int off, int len) + { + StringBuffer buf = new StringBuffer(); + + for (int i = off; i != off + len; i++) + { + if (bytes[i] >= ' ' && bytes[i] <= '~') + { + buf.append((char)bytes[i]); + } + } + + return buf.toString(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/util/DERDump.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/util/DERDump.java new file mode 100644 index 00000000..943cd2b5 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/util/DERDump.java @@ -0,0 +1,41 @@ +package org.bouncycastle.asn1.util; + +import org.bouncycastle.asn1.DEREncodable; +import org.bouncycastle.asn1.DERObject; + +/** + * @deprecated use ASN1Dump. + */ +public class DERDump + extends ASN1Dump +{ + /** + * dump out a DER object as a formatted string + * + * @param obj the DERObject to be dumped out. + */ + public static String dumpAsString( + DERObject obj) + { + StringBuffer buf = new StringBuffer(); + + _dumpAsString("", false, obj, buf); + + return buf.toString(); + } + + /** + * dump out a DER object as a formatted string + * + * @param obj the DERObject to be dumped out. + */ + public static String dumpAsString( + DEREncodable obj) + { + StringBuffer buf = new StringBuffer(); + + _dumpAsString("", false, obj.getDERObject(), buf); + + return buf.toString(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/util/Dump.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/util/Dump.java new file mode 100644 index 00000000..27a37f34 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/asn1/util/Dump.java @@ -0,0 +1,22 @@ +package org.bouncycastle.asn1.util; + +import java.io.FileInputStream; + +import org.bouncycastle.asn1.ASN1InputStream; + +public class Dump +{ + public static void main( + String args[]) + throws Exception + { + FileInputStream fIn = new FileInputStream(args[0]); + ASN1InputStream bIn = new ASN1InputStream(fIn); + Object obj = null; + + while ((obj = bIn.readObject()) != null) + { + System.out.println(ASN1Dump.dumpAsString(obj)); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/BlockCipher.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/BlockCipher.java new file mode 100644 index 00000000..3cfa25a0 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/BlockCipher.java @@ -0,0 +1,56 @@ +package org.bouncycastle.crypto; + + +/** + * Block cipher engines are expected to conform to this interface. + */ +public interface BlockCipher +{ + /** + * Initialise the cipher. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param params the key and other data required by the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException; + + /** + * Return the name of the algorithm the cipher implements. + * + * @return the name of the algorithm the cipher implements. + */ + public String getAlgorithmName(); + + /** + * Return the block size for this cipher (in bytes). + * + * @return the block size for this cipher in bytes. + */ + public int getBlockSize(); + + /** + * Process one block of input from the array in and write it to + * the out array. + * + * @param in the array containing the input data. + * @param inOff offset into the in array the data starts at. + * @param out the array the output data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception IllegalStateException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int processBlock(byte[] in, int inOff, byte[] out, int outOff) + throws DataLengthException, IllegalStateException; + + /** + * Reset the cipher. After resetting the cipher is in the same state + * as it was after the last init (if there was one). + */ + public void reset(); +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/BufferedBlockCipher.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/BufferedBlockCipher.java new file mode 100644 index 00000000..7f8adec7 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/BufferedBlockCipher.java @@ -0,0 +1,307 @@ +package org.bouncycastle.crypto; + + +/** + * A wrapper class that allows block ciphers to be used to process data in + * a piecemeal fashion. The BufferedBlockCipher outputs a block only when the + * buffer is full and more data is being added, or on a doFinal. + *

+ * Note: in the case where the underlying cipher is either a CFB cipher or an + * OFB one the last block may not be a multiple of the block size. + */ +public class BufferedBlockCipher +{ + protected byte[] buf; + protected int bufOff; + + protected boolean forEncryption; + protected BlockCipher cipher; + + protected boolean partialBlockOkay; + protected boolean pgpCFB; + + /** + * constructor for subclasses + */ + protected BufferedBlockCipher() + { + } + + /** + * Create a buffered block cipher without padding. + * + * @param cipher the underlying block cipher this buffering object wraps. + */ + public BufferedBlockCipher( + BlockCipher cipher) + { + this.cipher = cipher; + + buf = new byte[cipher.getBlockSize()]; + bufOff = 0; + + // + // check if we can handle partial blocks on doFinal. + // + String name = cipher.getAlgorithmName(); + int idx = name.indexOf('/') + 1; + + pgpCFB = (idx > 0 && name.startsWith("PGP", idx)); + + if (pgpCFB) + { + partialBlockOkay = true; + } + else + { + partialBlockOkay = (idx > 0 && (name.startsWith("CFB", idx) || name.startsWith("OFB", idx) || name.startsWith("OpenPGP", idx) || name.startsWith("SIC", idx) || name.startsWith("GCTR", idx))); + } + } + + /** + * return the cipher this object wraps. + * + * @return the cipher this object wraps. + */ + public BlockCipher getUnderlyingCipher() + { + return cipher; + } + + /** + * initialise the cipher. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param params the key and other data required by the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean forEncryption, + CipherParameters params) + throws IllegalArgumentException + { + this.forEncryption = forEncryption; + + reset(); + + cipher.init(forEncryption, params); + } + + /** + * return the blocksize for the underlying cipher. + * + * @return the blocksize for the underlying cipher. + */ + public int getBlockSize() + { + return cipher.getBlockSize(); + } + + /** + * return the size of the output buffer required for an update + * an input of len bytes. + * + * @param len the length of the input. + * @return the space required to accommodate a call to update + * with len bytes of input. + */ + public int getUpdateOutputSize( + int len) + { + int total = len + bufOff; + int leftOver; + + if (pgpCFB) + { + leftOver = total % buf.length - (cipher.getBlockSize() + 2); + } + else + { + leftOver = total % buf.length; + } + + return total - leftOver; + } + + /** + * return the size of the output buffer required for an update plus a + * doFinal with an input of 'length' bytes. + * + * @param length the length of the input. + * @return the space required to accommodate a call to update and doFinal + * with 'length' bytes of input. + */ + public int getOutputSize( + int length) + { + // Note: Can assume partialBlockOkay is true for purposes of this calculation + return length + bufOff; + } + + /** + * process a single byte, producing an output block if neccessary. + * + * @param in the input byte. + * @param out the space for any output that might be produced. + * @param outOff the offset from which the output will be copied. + * @return the number of output bytes copied to out. + * @exception DataLengthException if there isn't enough space in out. + * @exception IllegalStateException if the cipher isn't initialised. + */ + public int processByte( + byte in, + byte[] out, + int outOff) + throws DataLengthException, IllegalStateException + { + int resultLen = 0; + + buf[bufOff++] = in; + + if (bufOff == buf.length) + { + resultLen = cipher.processBlock(buf, 0, out, outOff); + bufOff = 0; + } + + return resultLen; + } + + /** + * process an array of bytes, producing output if necessary. + * + * @param in the input byte array. + * @param inOff the offset at which the input data starts. + * @param len the number of bytes to be copied out of the input array. + * @param out the space for any output that might be produced. + * @param outOff the offset from which the output will be copied. + * @return the number of output bytes copied to out. + * @exception DataLengthException if there isn't enough space in out. + * @exception IllegalStateException if the cipher isn't initialised. + */ + public int processBytes( + byte[] in, + int inOff, + int len, + byte[] out, + int outOff) + throws DataLengthException, IllegalStateException + { + if (len < 0) + { + throw new IllegalArgumentException("Can't have a negative input length!"); + } + + int blockSize = getBlockSize(); + int length = getUpdateOutputSize(len); + + if (length > 0) + { + if ((outOff + length) > out.length) + { + throw new DataLengthException("output buffer too short"); + } + } + + int resultLen = 0; + int gapLen = buf.length - bufOff; + + if (len > gapLen) + { + System.arraycopy(in, inOff, buf, bufOff, gapLen); + + resultLen += cipher.processBlock(buf, 0, out, outOff); + + bufOff = 0; + len -= gapLen; + inOff += gapLen; + + while (len > buf.length) + { + resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen); + + len -= blockSize; + inOff += blockSize; + } + } + + System.arraycopy(in, inOff, buf, bufOff, len); + + bufOff += len; + + if (bufOff == buf.length) + { + resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen); + bufOff = 0; + } + + return resultLen; + } + + /** + * Process the last block in the buffer. + * + * @param out the array the block currently being held is copied into. + * @param outOff the offset at which the copying starts. + * @return the number of output bytes copied to out. + * @exception DataLengthException if there is insufficient space in out for + * the output, or the input is not block size aligned and should be. + * @exception IllegalStateException if the underlying cipher is not + * initialised. + * @exception InvalidCipherTextException if padding is expected and not found. + * @exception DataLengthException if the input is not block size + * aligned. + */ + public int doFinal( + byte[] out, + int outOff) + throws DataLengthException, IllegalStateException, InvalidCipherTextException + { + int resultLen = 0; + + if (outOff + bufOff > out.length) + { + throw new DataLengthException("output buffer too short for doFinal()"); + } + + if (bufOff != 0 && partialBlockOkay) + { + cipher.processBlock(buf, 0, buf, 0); + resultLen = bufOff; + bufOff = 0; + System.arraycopy(buf, 0, out, outOff, resultLen); + } + else if (bufOff != 0) + { + throw new DataLengthException("data not block size aligned"); + } + + reset(); + + return resultLen; + } + + /** + * Reset the buffer and cipher. After resetting the object is in the same + * state as it was after the last init (if there was one). + */ + public void reset() + { + // + // clean the buffer. + // + for (int i = 0; i < buf.length; i++) + { + buf[i] = 0; + } + + bufOff = 0; + + // + // reset the underlying cipher. + // + cipher.reset(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/CipherParameters.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/CipherParameters.java new file mode 100644 index 00000000..5be87304 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/CipherParameters.java @@ -0,0 +1,8 @@ +package org.bouncycastle.crypto; + +/** + * all parameter classes implement this. + */ +public interface CipherParameters +{ +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/CryptoException.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/CryptoException.java new file mode 100644 index 00000000..cafc4cfa --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/CryptoException.java @@ -0,0 +1,27 @@ +package org.bouncycastle.crypto; + +/** + * the foundation class for the hard exceptions thrown by the crypto packages. + */ +@SuppressWarnings("serial") +public class CryptoException + extends Exception +{ + /** + * base constructor. + */ + public CryptoException() + { + } + + /** + * create a CryptoException with the given message. + * + * @param message the message to be carried with the exception. + */ + public CryptoException( + String message) + { + super(message); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/DataLengthException.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/DataLengthException.java new file mode 100644 index 00000000..29b37ae4 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/DataLengthException.java @@ -0,0 +1,30 @@ +package org.bouncycastle.crypto; + +/** + * this exception is thrown if a buffer that is meant to have output + * copied into it turns out to be too short, or if we've been given + * insufficient input. In general this exception will get thrown rather + * than an ArrayOutOfBounds exception. + */ +@SuppressWarnings("serial") +public class DataLengthException + extends RuntimeCryptoException +{ + /** + * base constructor. + */ + public DataLengthException() + { + } + + /** + * create a DataLengthException with the given message. + * + * @param message the message to be carried with the exception. + */ + public DataLengthException( + String message) + { + super(message); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/Digest.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/Digest.java new file mode 100644 index 00000000..f44fad0d --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/Digest.java @@ -0,0 +1,51 @@ +package org.bouncycastle.crypto; + +/** + * interface that a message digest conforms to. + */ +public interface Digest +{ + /** + * return the algorithm name + * + * @return the algorithm name + */ + public String getAlgorithmName(); + + /** + * return the size, in bytes, of the digest produced by this message digest. + * + * @return the size, in bytes, of the digest produced by this message digest. + */ + public int getDigestSize(); + + /** + * update the message digest with a single byte. + * + * @param in the input byte to be entered. + */ + public void update(byte in); + + /** + * update the message digest with a block of bytes. + * + * @param in the byte array containing the data. + * @param inOff the offset into the byte array where the data starts. + * @param len the length of the data. + */ + public void update(byte[] in, int inOff, int len); + + /** + * close the digest, producing the final digest value. The doFinal + * call leaves the digest reset. + * + * @param out the array the digest is to be copied into. + * @param outOff the offset into the out array the digest is to start at. + */ + public int doFinal(byte[] out, int outOff); + + /** + * reset the digest back to it's initial state. + */ + public void reset(); +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/ExtendedDigest.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/ExtendedDigest.java new file mode 100644 index 00000000..c5e9e8b0 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/ExtendedDigest.java @@ -0,0 +1,13 @@ +package org.bouncycastle.crypto; + +public interface ExtendedDigest + extends Digest +{ + /** + * Return the size in bytes of the internal buffer the digest applies it's compression + * function to. + * + * @return byte length of the digests internal buffer. + */ + public int getByteLength(); +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/InvalidCipherTextException.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/InvalidCipherTextException.java new file mode 100644 index 00000000..6d4b4141 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/InvalidCipherTextException.java @@ -0,0 +1,28 @@ +package org.bouncycastle.crypto; + +/** + * this exception is thrown whenever we find something we don't expect in a + * message. + */ +@SuppressWarnings("serial") +public class InvalidCipherTextException + extends CryptoException +{ + /** + * base constructor. + */ + public InvalidCipherTextException() + { + } + + /** + * create a InvalidCipherTextException with the given message. + * + * @param message the message to be carried with the exception. + */ + public InvalidCipherTextException( + String message) + { + super(message); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/Mac.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/Mac.java new file mode 100644 index 00000000..c00cd58c --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/Mac.java @@ -0,0 +1,71 @@ +package org.bouncycastle.crypto; + + +/** + * The base interface for implementations of message authentication codes (MACs). + */ +public interface Mac +{ + /** + * Initialise the MAC. + * + * @param params the key and other data required by the MAC. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init(CipherParameters params) + throws IllegalArgumentException; + + /** + * Return the name of the algorithm the MAC implements. + * + * @return the name of the algorithm the MAC implements. + */ + public String getAlgorithmName(); + + /** + * Return the block size for this MAC (in bytes). + * + * @return the block size for this MAC in bytes. + */ + public int getMacSize(); + + /** + * add a single byte to the mac for processing. + * + * @param in the byte to be processed. + * @exception IllegalStateException if the MAC is not initialised. + */ + public void update(byte in) + throws IllegalStateException; + + /** + * @param in the array containing the input. + * @param inOff the index in the array the data begins at. + * @param len the length of the input starting at inOff. + * @exception IllegalStateException if the MAC is not initialised. + * @exception DataLengthException if there isn't enough data in in. + */ + public void update(byte[] in, int inOff, int len) + throws DataLengthException, IllegalStateException; + + /** + * Compute the final stage of the MAC writing the output to the out + * parameter. + *

+ * doFinal leaves the MAC in the same state it was after the last init. + * + * @param out the array the MAC is to be output to. + * @param outOff the offset into the out buffer the output is to start at. + * @exception DataLengthException if there isn't enough space in out. + * @exception IllegalStateException if the MAC is not initialised. + */ + public int doFinal(byte[] out, int outOff) + throws DataLengthException, IllegalStateException; + + /** + * Reset the MAC. At the end of resetting the MAC should be in the + * in the same state it was after the last init (if there was one). + */ + public void reset(); +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/MaxBytesExceededException.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/MaxBytesExceededException.java new file mode 100644 index 00000000..5747f27c --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/MaxBytesExceededException.java @@ -0,0 +1,28 @@ +package org.bouncycastle.crypto; + +/** + * this exception is thrown whenever a cipher requires a change of key, iv + * or similar after x amount of bytes enciphered + */ +@SuppressWarnings("serial") +public class MaxBytesExceededException + extends RuntimeCryptoException +{ + /** + * base constructor. + */ + public MaxBytesExceededException() + { + } + + /** + * create an with the given message. + * + * @param message the message to be carried with the exception. + */ + public MaxBytesExceededException( + String message) + { + super(message); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/PBEParametersGenerator.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/PBEParametersGenerator.java new file mode 100644 index 00000000..82eaa5f6 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/PBEParametersGenerator.java @@ -0,0 +1,157 @@ +package org.bouncycastle.crypto; + +import org.bouncycastle.util.Strings; + +/** + * super class for all Password Based Encryption (PBE) parameter generator classes. + */ +public abstract class PBEParametersGenerator +{ + protected byte[] password; + protected byte[] salt; + protected int iterationCount; + + /** + * base constructor. + */ + protected PBEParametersGenerator() + { + } + + /** + * initialise the PBE generator. + * + * @param password the password converted into bytes (see below). + * @param salt the salt to be mixed with the password. + * @param iterationCount the number of iterations the "mixing" function + * is to be applied for. + */ + public void init( + byte[] password, + byte[] salt, + int iterationCount) + { + this.password = password; + this.salt = salt; + this.iterationCount = iterationCount; + } + + /** + * return the password byte array. + * + * @return the password byte array. + */ + public byte[] getPassword() + { + return password; + } + + /** + * return the salt byte array. + * + * @return the salt byte array. + */ + public byte[] getSalt() + { + return salt; + } + + /** + * return the iteration count. + * + * @return the iteration count. + */ + public int getIterationCount() + { + return iterationCount; + } + + /** + * generate derived parameters for a key of length keySize. + * + * @param keySize the length, in bits, of the key required. + * @return a parameters object representing a key. + */ + public abstract CipherParameters generateDerivedParameters(int keySize); + + /** + * generate derived parameters for a key of length keySize, and + * an initialisation vector (IV) of length ivSize. + * + * @param keySize the length, in bits, of the key required. + * @param ivSize the length, in bits, of the iv required. + * @return a parameters object representing a key and an IV. + */ + public abstract CipherParameters generateDerivedParameters(int keySize, int ivSize); + + /** + * generate derived parameters for a key of length keySize, specifically + * for use with a MAC. + * + * @param keySize the length, in bits, of the key required. + * @return a parameters object representing a key. + */ + public abstract CipherParameters generateDerivedMacParameters(int keySize); + + /** + * converts a password to a byte array according to the scheme in + * PKCS5 (ascii, no padding) + * + * @param password a character array reqpresenting the password. + * @return a byte array representing the password. + */ + public static byte[] PKCS5PasswordToBytes( + char[] password) + { + byte[] bytes = new byte[password.length]; + + for (int i = 0; i != bytes.length; i++) + { + bytes[i] = (byte)password[i]; + } + + return bytes; + } + + /** + * converts a password to a byte array according to the scheme in + * PKCS5 (UTF-8, no padding) + * + * @param password a character array reqpresenting the password. + * @return a byte array representing the password. + */ + public static byte[] PKCS5PasswordToUTF8Bytes( + char[] password) + { + return Strings.toUTF8ByteArray(password); + } + + /** + * converts a password to a byte array according to the scheme in + * PKCS12 (unicode, big endian, 2 zero pad bytes at the end). + * + * @param password a character array representing the password. + * @return a byte array representing the password. + */ + public static byte[] PKCS12PasswordToBytes( + char[] password) + { + if (password.length > 0) + { + // +1 for extra 2 pad bytes. + byte[] bytes = new byte[(password.length + 1) * 2]; + + for (int i = 0; i != password.length; i ++) + { + bytes[i * 2] = (byte)(password[i] >>> 8); + bytes[i * 2 + 1] = (byte)password[i]; + } + + return bytes; + } + else + { + return new byte[0]; + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/RuntimeCryptoException.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/RuntimeCryptoException.java new file mode 100644 index 00000000..8e782dee --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/RuntimeCryptoException.java @@ -0,0 +1,27 @@ +package org.bouncycastle.crypto; + +/** + * the foundation class for the exceptions thrown by the crypto packages. + */ +@SuppressWarnings("serial") +public class RuntimeCryptoException + extends RuntimeException +{ + /** + * base constructor. + */ + public RuntimeCryptoException() + { + } + + /** + * create a RuntimeCryptoException with the given message. + * + * @param message the message to be carried with the exception. + */ + public RuntimeCryptoException( + String message) + { + super(message); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/StreamBlockCipher.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/StreamBlockCipher.java new file mode 100644 index 00000000..8fdd2324 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/StreamBlockCipher.java @@ -0,0 +1,108 @@ +package org.bouncycastle.crypto; + +/** + * a wrapper for block ciphers with a single byte block size, so that they + * can be treated like stream ciphers. + */ +public class StreamBlockCipher + implements StreamCipher +{ + private BlockCipher cipher; + + private byte[] oneByte = new byte[1]; + + /** + * basic constructor. + * + * @param cipher the block cipher to be wrapped. + * @exception IllegalArgumentException if the cipher has a block size other than + * one. + */ + public StreamBlockCipher( + BlockCipher cipher) + { + if (cipher.getBlockSize() != 1) + { + throw new IllegalArgumentException("block cipher block size != 1."); + } + + this.cipher = cipher; + } + + /** + * initialise the underlying cipher. + * + * @param forEncryption true if we are setting up for encryption, false otherwise. + * @param params the necessary parameters for the underlying cipher to be initialised. + */ + public void init( + boolean forEncryption, + CipherParameters params) + { + cipher.init(forEncryption, params); + } + + /** + * return the name of the algorithm we are wrapping. + * + * @return the name of the algorithm we are wrapping. + */ + public String getAlgorithmName() + { + return cipher.getAlgorithmName(); + } + + /** + * encrypt/decrypt a single byte returning the result. + * + * @param in the byte to be processed. + * @return the result of processing the input byte. + */ + public byte returnByte( + byte in) + { + oneByte[0] = in; + + cipher.processBlock(oneByte, 0, oneByte, 0); + + return oneByte[0]; + } + + /** + * process a block of bytes from in putting the result into out. + * + * @param in the input byte array. + * @param inOff the offset into the in array where the data to be processed starts. + * @param len the number of bytes to be processed. + * @param out the output buffer the processed bytes go into. + * @param outOff the offset into the output byte array the processed data stars at. + * @exception DataLengthException if the output buffer is too small. + */ + public void processBytes( + byte[] in, + int inOff, + int len, + byte[] out, + int outOff) + throws DataLengthException + { + if (outOff + len > out.length) + { + throw new DataLengthException("output buffer too small in processBytes()"); + } + + for (int i = 0; i != len; i++) + { + cipher.processBlock(in, inOff + i, out, outOff + i); + } + } + + /** + * reset the underlying cipher. This leaves it in the same state + * it was at after the last init (if there was one). + */ + public void reset() + { + cipher.reset(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/StreamCipher.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/StreamCipher.java new file mode 100644 index 00000000..2a55d4f6 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/StreamCipher.java @@ -0,0 +1,53 @@ +package org.bouncycastle.crypto; + +/** + * the interface stream ciphers conform to. + */ +public interface StreamCipher +{ + /** + * Initialise the cipher. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param params the key and other data required by the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException; + + /** + * Return the name of the algorithm the cipher implements. + * + * @return the name of the algorithm the cipher implements. + */ + public String getAlgorithmName(); + + /** + * encrypt/decrypt a single byte returning the result. + * + * @param in the byte to be processed. + * @return the result of processing the input byte. + */ + public byte returnByte(byte in); + + /** + * process a block of bytes from in putting the result into out. + * + * @param in the input byte array. + * @param inOff the offset into the in array where the data to be processed starts. + * @param len the number of bytes to be processed. + * @param out the output buffer the processed bytes go into. + * @param outOff the offset into the output byte array the processed data starts at. + * @exception DataLengthException if the output buffer is too small. + */ + public void processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) + throws DataLengthException; + + /** + * reset the cipher. This leaves it in the same state + * it was at after the last init (if there was one). + */ + public void reset(); +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/Wrapper.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/Wrapper.java new file mode 100644 index 00000000..3956a6fc --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/Wrapper.java @@ -0,0 +1,18 @@ +package org.bouncycastle.crypto; + +public interface Wrapper +{ + public void init(boolean forWrapping, CipherParameters param); + + /** + * Return the name of the algorithm the wrapper implements. + * + * @return the name of the algorithm the wrapper implements. + */ + public String getAlgorithmName(); + + public byte[] wrap(byte[] in, int inOff, int inLen); + + public byte[] unwrap(byte[] in, int inOff, int inLen) + throws InvalidCipherTextException; +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/GeneralDigest.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/GeneralDigest.java new file mode 100644 index 00000000..f2c99674 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/GeneralDigest.java @@ -0,0 +1,135 @@ +package org.bouncycastle.crypto.digests; + +import org.bouncycastle.crypto.ExtendedDigest; + +/** + * base implementation of MD4 family style digest as outlined in + * "Handbook of Applied Cryptography", pages 344 - 347. + */ +public abstract class GeneralDigest + implements ExtendedDigest +{ + private static final int BYTE_LENGTH = 64; + private byte[] xBuf; + private int xBufOff; + + private long byteCount; + + /** + * Standard constructor + */ + protected GeneralDigest() + { + xBuf = new byte[4]; + xBufOff = 0; + } + + /** + * Copy constructor. We are using copy constructors in place + * of the Object.clone() interface as this interface is not + * supported by J2ME. + */ + protected GeneralDigest(GeneralDigest t) + { + xBuf = new byte[t.xBuf.length]; + System.arraycopy(t.xBuf, 0, xBuf, 0, t.xBuf.length); + + xBufOff = t.xBufOff; + byteCount = t.byteCount; + } + + public void update( + byte in) + { + xBuf[xBufOff++] = in; + + if (xBufOff == xBuf.length) + { + processWord(xBuf, 0); + xBufOff = 0; + } + + byteCount++; + } + + public void update( + byte[] in, + int inOff, + int len) + { + // + // fill the current word + // + while ((xBufOff != 0) && (len > 0)) + { + update(in[inOff]); + + inOff++; + len--; + } + + // + // process whole words. + // + while (len > xBuf.length) + { + processWord(in, inOff); + + inOff += xBuf.length; + len -= xBuf.length; + byteCount += xBuf.length; + } + + // + // load in the remainder. + // + while (len > 0) + { + update(in[inOff]); + + inOff++; + len--; + } + } + + public void finish() + { + long bitLength = (byteCount << 3); + + // + // add the pad bytes. + // + update((byte)128); + + while (xBufOff != 0) + { + update((byte)0); + } + + processLength(bitLength); + + processBlock(); + } + + public void reset() + { + byteCount = 0; + + xBufOff = 0; + for (int i = 0; i < xBuf.length; i++) + { + xBuf[i] = 0; + } + } + + public int getByteLength() + { + return BYTE_LENGTH; + } + + protected abstract void processWord(byte[] in, int inOff); + + protected abstract void processLength(long bitLength); + + protected abstract void processBlock(); +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/MD2Digest.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/MD2Digest.java new file mode 100644 index 00000000..0edafbc1 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/MD2Digest.java @@ -0,0 +1,237 @@ +package org.bouncycastle.crypto.digests; + +import org.bouncycastle.crypto.*; +/** + * implementation of MD2 + * as outlined in RFC1319 by B.Kaliski from RSA Laboratories April 1992 + */ +public class MD2Digest + implements ExtendedDigest +{ + private static final int DIGEST_LENGTH = 16; + + /* X buffer */ + private byte[] X = new byte[48]; + private int xOff; + /* M buffer */ + private byte[] M = new byte[16]; + private int mOff; + /* check sum */ + private byte[] C = new byte[16]; + private int COff; + + public MD2Digest() + { + reset(); + } + public MD2Digest(MD2Digest t) + { + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + System.arraycopy(t.M, 0, M, 0, t.M.length); + mOff = t.mOff; + System.arraycopy(t.C, 0, C, 0, t.C.length); + COff = t.COff; + } + /** + * return the algorithm name + * + * @return the algorithm name + */ + public String getAlgorithmName() + { + return "MD2"; + } + /** + * return the size, in bytes, of the digest produced by this message digest. + * + * @return the size, in bytes, of the digest produced by this message digest. + */ + public int getDigestSize() + { + return DIGEST_LENGTH; + } + /** + * close the digest, producing the final digest value. The doFinal + * call leaves the digest reset. + * + * @param out the array the digest is to be copied into. + * @param outOff the offset into the out array the digest is to start at. + */ + public int doFinal(byte[] out, int outOff) + { + // add padding + byte paddingByte = (byte)(M.length-mOff); + for (int i=mOff;i 0)) + { + update(in[inOff]); + inOff++; + len--; + } + + // + // process whole words. + // + while (len > 16) + { + System.arraycopy(in,inOff,M,0,16); + processCheckSum(M); + processBlock(M); + len -= 16; + inOff += 16; + } + + // + // load in the remainder. + // + while (len > 0) + { + update(in[inOff]); + inOff++; + len--; + } + } + protected void processCheckSum(byte[] m) + { + int L = C[15]; + for (int i=0;i<16;i++) + { + C[i] ^= S[(m[i] ^ L) & 0xff]; + L = C[i]; + } + } + protected void processBlock(byte[] m) + { + for (int i=0;i<16;i++) + { + X[i+16] = m[i]; + X[i+32] = (byte)(m[i] ^ X[i]); + } + // encrypt block + int t = 0; + + for (int j=0;j<18;j++) + { + for (int k=0;k<48;k++) + { + t = X[k] ^= S[t]; + t = t & 0xff; + } + t = (t + j)%256; + } + } + // 256-byte random permutation constructed from the digits of PI + private static final byte[] S = { + (byte)41,(byte)46,(byte)67,(byte)201,(byte)162,(byte)216,(byte)124, + (byte)1,(byte)61,(byte)54,(byte)84,(byte)161,(byte)236,(byte)240, + (byte)6,(byte)19,(byte)98,(byte)167,(byte)5,(byte)243,(byte)192, + (byte)199,(byte)115,(byte)140,(byte)152,(byte)147,(byte)43,(byte)217, + (byte)188,(byte)76,(byte)130,(byte)202,(byte)30,(byte)155,(byte)87, + (byte)60,(byte)253,(byte)212,(byte)224,(byte)22,(byte)103,(byte)66, + (byte)111,(byte)24,(byte)138,(byte)23,(byte)229,(byte)18,(byte)190, + (byte)78,(byte)196,(byte)214,(byte)218,(byte)158,(byte)222,(byte)73, + (byte)160,(byte)251,(byte)245,(byte)142,(byte)187,(byte)47,(byte)238, + (byte)122,(byte)169,(byte)104,(byte)121,(byte)145,(byte)21,(byte)178, + (byte)7,(byte)63,(byte)148,(byte)194,(byte)16,(byte)137,(byte)11, + (byte)34,(byte)95,(byte)33,(byte)128,(byte)127,(byte)93,(byte)154, + (byte)90,(byte)144,(byte)50,(byte)39,(byte)53,(byte)62,(byte)204, + (byte)231,(byte)191,(byte)247,(byte)151,(byte)3,(byte)255,(byte)25, + (byte)48,(byte)179,(byte)72,(byte)165,(byte)181,(byte)209,(byte)215, + (byte)94,(byte)146,(byte)42,(byte)172,(byte)86,(byte)170,(byte)198, + (byte)79,(byte)184,(byte)56,(byte)210,(byte)150,(byte)164,(byte)125, + (byte)182,(byte)118,(byte)252,(byte)107,(byte)226,(byte)156,(byte)116, + (byte)4,(byte)241,(byte)69,(byte)157,(byte)112,(byte)89,(byte)100, + (byte)113,(byte)135,(byte)32,(byte)134,(byte)91,(byte)207,(byte)101, + (byte)230,(byte)45,(byte)168,(byte)2,(byte)27,(byte)96,(byte)37, + (byte)173,(byte)174,(byte)176,(byte)185,(byte)246,(byte)28,(byte)70, + (byte)97,(byte)105,(byte)52,(byte)64,(byte)126,(byte)15,(byte)85, + (byte)71,(byte)163,(byte)35,(byte)221,(byte)81,(byte)175,(byte)58, + (byte)195,(byte)92,(byte)249,(byte)206,(byte)186,(byte)197,(byte)234, + (byte)38,(byte)44,(byte)83,(byte)13,(byte)110,(byte)133,(byte)40, + (byte)132, 9,(byte)211,(byte)223,(byte)205,(byte)244,(byte)65, + (byte)129,(byte)77,(byte)82,(byte)106,(byte)220,(byte)55,(byte)200, + (byte)108,(byte)193,(byte)171,(byte)250,(byte)36,(byte)225,(byte)123, + (byte)8,(byte)12,(byte)189,(byte)177,(byte)74,(byte)120,(byte)136, + (byte)149,(byte)139,(byte)227,(byte)99,(byte)232,(byte)109,(byte)233, + (byte)203,(byte)213,(byte)254,(byte)59,(byte)0,(byte)29,(byte)57, + (byte)242,(byte)239,(byte)183,(byte)14,(byte)102,(byte)88,(byte)208, + (byte)228,(byte)166,(byte)119,(byte)114,(byte)248,(byte)235,(byte)117, + (byte)75,(byte)10,(byte)49,(byte)68,(byte)80,(byte)180,(byte)143, + (byte)237,(byte)31,(byte)26,(byte)219,(byte)153,(byte)141,(byte)51, + (byte)159,(byte)17,(byte)131,(byte)20 + }; + + public int getByteLength() + { + return 16; + } +} + + diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/MD5Digest.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/MD5Digest.java new file mode 100644 index 00000000..05ed27a6 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/MD5Digest.java @@ -0,0 +1,302 @@ +package org.bouncycastle.crypto.digests; + + +/** + * implementation of MD5 as outlined in "Handbook of Applied Cryptography", pages 346 - 347. + */ +public class MD5Digest + extends GeneralDigest +{ + private static final int DIGEST_LENGTH = 16; + + private int H1, H2, H3, H4; // IV's + + private int[] X = new int[16]; + private int xOff; + + /** + * Standard constructor + */ + public MD5Digest() + { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public MD5Digest(MD5Digest t) + { + super(t); + + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + public String getAlgorithmName() + { + return "MD5"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8) + | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + processBlock(); + } + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)(bitLength >>> 32); + } + + private void unpackWord( + int word, + byte[] out, + int outOff) + { + out[outOff] = (byte)word; + out[outOff + 1] = (byte)(word >>> 8); + out[outOff + 2] = (byte)(word >>> 16); + out[outOff + 3] = (byte)(word >>> 24); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + unpackWord(H1, out, outOff); + unpackWord(H2, out, outOff + 4); + unpackWord(H3, out, outOff + 8); + unpackWord(H4, out, outOff + 12); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables to the IV values. + */ + public void reset() + { + super.reset(); + + H1 = 0x67452301; + H2 = 0xefcdab89; + H3 = 0x98badcfe; + H4 = 0x10325476; + + xOff = 0; + + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + // + // round 1 left rotates + // + private static final int S11 = 7; + private static final int S12 = 12; + private static final int S13 = 17; + private static final int S14 = 22; + + // + // round 2 left rotates + // + private static final int S21 = 5; + private static final int S22 = 9; + private static final int S23 = 14; + private static final int S24 = 20; + + // + // round 3 left rotates + // + private static final int S31 = 4; + private static final int S32 = 11; + private static final int S33 = 16; + private static final int S34 = 23; + + // + // round 4 left rotates + // + private static final int S41 = 6; + private static final int S42 = 10; + private static final int S43 = 15; + private static final int S44 = 21; + + /* + * rotate int x left n bits. + */ + private int rotateLeft( + int x, + int n) + { + return (x << n) | (x >>> (32 - n)); + } + + /* + * F, G, H and I are the basic MD5 functions. + */ + private int F( + int u, + int v, + int w) + { + return (u & v) | (~u & w); + } + + private int G( + int u, + int v, + int w) + { + return (u & w) | (v & ~w); + } + + private int H( + int u, + int v, + int w) + { + return u ^ v ^ w; + } + + private int K( + int u, + int v, + int w) + { + return v ^ (u | ~w); + } + + protected void processBlock() + { + int a = H1; + int b = H2; + int c = H3; + int d = H4; + + // + // Round 1 - F cycle, 16 times. + // + a = rotateLeft(a + F(b, c, d) + X[ 0] + 0xd76aa478, S11) + b; + d = rotateLeft(d + F(a, b, c) + X[ 1] + 0xe8c7b756, S12) + a; + c = rotateLeft(c + F(d, a, b) + X[ 2] + 0x242070db, S13) + d; + b = rotateLeft(b + F(c, d, a) + X[ 3] + 0xc1bdceee, S14) + c; + a = rotateLeft(a + F(b, c, d) + X[ 4] + 0xf57c0faf, S11) + b; + d = rotateLeft(d + F(a, b, c) + X[ 5] + 0x4787c62a, S12) + a; + c = rotateLeft(c + F(d, a, b) + X[ 6] + 0xa8304613, S13) + d; + b = rotateLeft(b + F(c, d, a) + X[ 7] + 0xfd469501, S14) + c; + a = rotateLeft(a + F(b, c, d) + X[ 8] + 0x698098d8, S11) + b; + d = rotateLeft(d + F(a, b, c) + X[ 9] + 0x8b44f7af, S12) + a; + c = rotateLeft(c + F(d, a, b) + X[10] + 0xffff5bb1, S13) + d; + b = rotateLeft(b + F(c, d, a) + X[11] + 0x895cd7be, S14) + c; + a = rotateLeft(a + F(b, c, d) + X[12] + 0x6b901122, S11) + b; + d = rotateLeft(d + F(a, b, c) + X[13] + 0xfd987193, S12) + a; + c = rotateLeft(c + F(d, a, b) + X[14] + 0xa679438e, S13) + d; + b = rotateLeft(b + F(c, d, a) + X[15] + 0x49b40821, S14) + c; + + // + // Round 2 - G cycle, 16 times. + // + a = rotateLeft(a + G(b, c, d) + X[ 1] + 0xf61e2562, S21) + b; + d = rotateLeft(d + G(a, b, c) + X[ 6] + 0xc040b340, S22) + a; + c = rotateLeft(c + G(d, a, b) + X[11] + 0x265e5a51, S23) + d; + b = rotateLeft(b + G(c, d, a) + X[ 0] + 0xe9b6c7aa, S24) + c; + a = rotateLeft(a + G(b, c, d) + X[ 5] + 0xd62f105d, S21) + b; + d = rotateLeft(d + G(a, b, c) + X[10] + 0x02441453, S22) + a; + c = rotateLeft(c + G(d, a, b) + X[15] + 0xd8a1e681, S23) + d; + b = rotateLeft(b + G(c, d, a) + X[ 4] + 0xe7d3fbc8, S24) + c; + a = rotateLeft(a + G(b, c, d) + X[ 9] + 0x21e1cde6, S21) + b; + d = rotateLeft(d + G(a, b, c) + X[14] + 0xc33707d6, S22) + a; + c = rotateLeft(c + G(d, a, b) + X[ 3] + 0xf4d50d87, S23) + d; + b = rotateLeft(b + G(c, d, a) + X[ 8] + 0x455a14ed, S24) + c; + a = rotateLeft(a + G(b, c, d) + X[13] + 0xa9e3e905, S21) + b; + d = rotateLeft(d + G(a, b, c) + X[ 2] + 0xfcefa3f8, S22) + a; + c = rotateLeft(c + G(d, a, b) + X[ 7] + 0x676f02d9, S23) + d; + b = rotateLeft(b + G(c, d, a) + X[12] + 0x8d2a4c8a, S24) + c; + + // + // Round 3 - H cycle, 16 times. + // + a = rotateLeft(a + H(b, c, d) + X[ 5] + 0xfffa3942, S31) + b; + d = rotateLeft(d + H(a, b, c) + X[ 8] + 0x8771f681, S32) + a; + c = rotateLeft(c + H(d, a, b) + X[11] + 0x6d9d6122, S33) + d; + b = rotateLeft(b + H(c, d, a) + X[14] + 0xfde5380c, S34) + c; + a = rotateLeft(a + H(b, c, d) + X[ 1] + 0xa4beea44, S31) + b; + d = rotateLeft(d + H(a, b, c) + X[ 4] + 0x4bdecfa9, S32) + a; + c = rotateLeft(c + H(d, a, b) + X[ 7] + 0xf6bb4b60, S33) + d; + b = rotateLeft(b + H(c, d, a) + X[10] + 0xbebfbc70, S34) + c; + a = rotateLeft(a + H(b, c, d) + X[13] + 0x289b7ec6, S31) + b; + d = rotateLeft(d + H(a, b, c) + X[ 0] + 0xeaa127fa, S32) + a; + c = rotateLeft(c + H(d, a, b) + X[ 3] + 0xd4ef3085, S33) + d; + b = rotateLeft(b + H(c, d, a) + X[ 6] + 0x04881d05, S34) + c; + a = rotateLeft(a + H(b, c, d) + X[ 9] + 0xd9d4d039, S31) + b; + d = rotateLeft(d + H(a, b, c) + X[12] + 0xe6db99e5, S32) + a; + c = rotateLeft(c + H(d, a, b) + X[15] + 0x1fa27cf8, S33) + d; + b = rotateLeft(b + H(c, d, a) + X[ 2] + 0xc4ac5665, S34) + c; + + // + // Round 4 - K cycle, 16 times. + // + a = rotateLeft(a + K(b, c, d) + X[ 0] + 0xf4292244, S41) + b; + d = rotateLeft(d + K(a, b, c) + X[ 7] + 0x432aff97, S42) + a; + c = rotateLeft(c + K(d, a, b) + X[14] + 0xab9423a7, S43) + d; + b = rotateLeft(b + K(c, d, a) + X[ 5] + 0xfc93a039, S44) + c; + a = rotateLeft(a + K(b, c, d) + X[12] + 0x655b59c3, S41) + b; + d = rotateLeft(d + K(a, b, c) + X[ 3] + 0x8f0ccc92, S42) + a; + c = rotateLeft(c + K(d, a, b) + X[10] + 0xffeff47d, S43) + d; + b = rotateLeft(b + K(c, d, a) + X[ 1] + 0x85845dd1, S44) + c; + a = rotateLeft(a + K(b, c, d) + X[ 8] + 0x6fa87e4f, S41) + b; + d = rotateLeft(d + K(a, b, c) + X[15] + 0xfe2ce6e0, S42) + a; + c = rotateLeft(c + K(d, a, b) + X[ 6] + 0xa3014314, S43) + d; + b = rotateLeft(b + K(c, d, a) + X[13] + 0x4e0811a1, S44) + c; + a = rotateLeft(a + K(b, c, d) + X[ 4] + 0xf7537e82, S41) + b; + d = rotateLeft(d + K(a, b, c) + X[11] + 0xbd3af235, S42) + a; + c = rotateLeft(c + K(d, a, b) + X[ 2] + 0x2ad7d2bb, S43) + d; + b = rotateLeft(b + K(c, d, a) + X[ 9] + 0xeb86d391, S44) + c; + + H1 += a; + H2 += b; + H3 += c; + H4 += d; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/RIPEMD160Digest.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/RIPEMD160Digest.java new file mode 100644 index 00000000..2351357c --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/RIPEMD160Digest.java @@ -0,0 +1,422 @@ +package org.bouncycastle.crypto.digests; + + +/** + * implementation of RIPEMD see, + * http://www.esat.kuleuven.ac.be/~bosselae/ripemd160.html + */ +public class RIPEMD160Digest + extends GeneralDigest +{ + private static final int DIGEST_LENGTH = 20; + + private int H0, H1, H2, H3, H4; // IV's + + private int[] X = new int[16]; + private int xOff; + + /** + * Standard constructor + */ + public RIPEMD160Digest() + { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public RIPEMD160Digest(RIPEMD160Digest t) + { + super(t); + + H0 = t.H0; + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + public String getAlgorithmName() + { + return "RIPEMD160"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + X[xOff++] = (in[inOff] & 0xff) | ((in[inOff + 1] & 0xff) << 8) + | ((in[inOff + 2] & 0xff) << 16) | ((in[inOff + 3] & 0xff) << 24); + + if (xOff == 16) + { + processBlock(); + } + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int)(bitLength & 0xffffffff); + X[15] = (int)(bitLength >>> 32); + } + + private void unpackWord( + int word, + byte[] out, + int outOff) + { + out[outOff] = (byte)word; + out[outOff + 1] = (byte)(word >>> 8); + out[outOff + 2] = (byte)(word >>> 16); + out[outOff + 3] = (byte)(word >>> 24); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + unpackWord(H0, out, outOff); + unpackWord(H1, out, outOff + 4); + unpackWord(H2, out, outOff + 8); + unpackWord(H3, out, outOff + 12); + unpackWord(H4, out, outOff + 16); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables to the IV values. + */ + public void reset() + { + super.reset(); + + H0 = 0x67452301; + H1 = 0xefcdab89; + H2 = 0x98badcfe; + H3 = 0x10325476; + H4 = 0xc3d2e1f0; + + xOff = 0; + + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + /* + * rotate int x left n bits. + */ + private int RL( + int x, + int n) + { + return (x << n) | (x >>> (32 - n)); + } + + /* + * f1,f2,f3,f4,f5 are the basic RIPEMD160 functions. + */ + + /* + * rounds 0-15 + */ + private int f1( + int x, + int y, + int z) + { + return x ^ y ^ z; + } + + /* + * rounds 16-31 + */ + private int f2( + int x, + int y, + int z) + { + return (x & y) | (~x & z); + } + + /* + * rounds 32-47 + */ + private int f3( + int x, + int y, + int z) + { + return (x | ~y) ^ z; + } + + /* + * rounds 48-63 + */ + private int f4( + int x, + int y, + int z) + { + return (x & z) | (y & ~z); + } + + /* + * rounds 64-79 + */ + private int f5( + int x, + int y, + int z) + { + return x ^ (y | ~z); + } + + protected void processBlock() + { + int a, aa; + int b, bb; + int c, cc; + int d, dd; + int e, ee; + + a = aa = H0; + b = bb = H1; + c = cc = H2; + d = dd = H3; + e = ee = H4; + + // + // Rounds 1 - 16 + // + // left + a = RL(a + f1(b,c,d) + X[ 0], 11) + e; c = RL(c, 10); + e = RL(e + f1(a,b,c) + X[ 1], 14) + d; b = RL(b, 10); + d = RL(d + f1(e,a,b) + X[ 2], 15) + c; a = RL(a, 10); + c = RL(c + f1(d,e,a) + X[ 3], 12) + b; e = RL(e, 10); + b = RL(b + f1(c,d,e) + X[ 4], 5) + a; d = RL(d, 10); + a = RL(a + f1(b,c,d) + X[ 5], 8) + e; c = RL(c, 10); + e = RL(e + f1(a,b,c) + X[ 6], 7) + d; b = RL(b, 10); + d = RL(d + f1(e,a,b) + X[ 7], 9) + c; a = RL(a, 10); + c = RL(c + f1(d,e,a) + X[ 8], 11) + b; e = RL(e, 10); + b = RL(b + f1(c,d,e) + X[ 9], 13) + a; d = RL(d, 10); + a = RL(a + f1(b,c,d) + X[10], 14) + e; c = RL(c, 10); + e = RL(e + f1(a,b,c) + X[11], 15) + d; b = RL(b, 10); + d = RL(d + f1(e,a,b) + X[12], 6) + c; a = RL(a, 10); + c = RL(c + f1(d,e,a) + X[13], 7) + b; e = RL(e, 10); + b = RL(b + f1(c,d,e) + X[14], 9) + a; d = RL(d, 10); + a = RL(a + f1(b,c,d) + X[15], 8) + e; c = RL(c, 10); + + // right + aa = RL(aa + f5(bb,cc,dd) + X[ 5] + 0x50a28be6, 8) + ee; cc = RL(cc, 10); + ee = RL(ee + f5(aa,bb,cc) + X[14] + 0x50a28be6, 9) + dd; bb = RL(bb, 10); + dd = RL(dd + f5(ee,aa,bb) + X[ 7] + 0x50a28be6, 9) + cc; aa = RL(aa, 10); + cc = RL(cc + f5(dd,ee,aa) + X[ 0] + 0x50a28be6, 11) + bb; ee = RL(ee, 10); + bb = RL(bb + f5(cc,dd,ee) + X[ 9] + 0x50a28be6, 13) + aa; dd = RL(dd, 10); + aa = RL(aa + f5(bb,cc,dd) + X[ 2] + 0x50a28be6, 15) + ee; cc = RL(cc, 10); + ee = RL(ee + f5(aa,bb,cc) + X[11] + 0x50a28be6, 15) + dd; bb = RL(bb, 10); + dd = RL(dd + f5(ee,aa,bb) + X[ 4] + 0x50a28be6, 5) + cc; aa = RL(aa, 10); + cc = RL(cc + f5(dd,ee,aa) + X[13] + 0x50a28be6, 7) + bb; ee = RL(ee, 10); + bb = RL(bb + f5(cc,dd,ee) + X[ 6] + 0x50a28be6, 7) + aa; dd = RL(dd, 10); + aa = RL(aa + f5(bb,cc,dd) + X[15] + 0x50a28be6, 8) + ee; cc = RL(cc, 10); + ee = RL(ee + f5(aa,bb,cc) + X[ 8] + 0x50a28be6, 11) + dd; bb = RL(bb, 10); + dd = RL(dd + f5(ee,aa,bb) + X[ 1] + 0x50a28be6, 14) + cc; aa = RL(aa, 10); + cc = RL(cc + f5(dd,ee,aa) + X[10] + 0x50a28be6, 14) + bb; ee = RL(ee, 10); + bb = RL(bb + f5(cc,dd,ee) + X[ 3] + 0x50a28be6, 12) + aa; dd = RL(dd, 10); + aa = RL(aa + f5(bb,cc,dd) + X[12] + 0x50a28be6, 6) + ee; cc = RL(cc, 10); + + // + // Rounds 16-31 + // + // left + e = RL(e + f2(a,b,c) + X[ 7] + 0x5a827999, 7) + d; b = RL(b, 10); + d = RL(d + f2(e,a,b) + X[ 4] + 0x5a827999, 6) + c; a = RL(a, 10); + c = RL(c + f2(d,e,a) + X[13] + 0x5a827999, 8) + b; e = RL(e, 10); + b = RL(b + f2(c,d,e) + X[ 1] + 0x5a827999, 13) + a; d = RL(d, 10); + a = RL(a + f2(b,c,d) + X[10] + 0x5a827999, 11) + e; c = RL(c, 10); + e = RL(e + f2(a,b,c) + X[ 6] + 0x5a827999, 9) + d; b = RL(b, 10); + d = RL(d + f2(e,a,b) + X[15] + 0x5a827999, 7) + c; a = RL(a, 10); + c = RL(c + f2(d,e,a) + X[ 3] + 0x5a827999, 15) + b; e = RL(e, 10); + b = RL(b + f2(c,d,e) + X[12] + 0x5a827999, 7) + a; d = RL(d, 10); + a = RL(a + f2(b,c,d) + X[ 0] + 0x5a827999, 12) + e; c = RL(c, 10); + e = RL(e + f2(a,b,c) + X[ 9] + 0x5a827999, 15) + d; b = RL(b, 10); + d = RL(d + f2(e,a,b) + X[ 5] + 0x5a827999, 9) + c; a = RL(a, 10); + c = RL(c + f2(d,e,a) + X[ 2] + 0x5a827999, 11) + b; e = RL(e, 10); + b = RL(b + f2(c,d,e) + X[14] + 0x5a827999, 7) + a; d = RL(d, 10); + a = RL(a + f2(b,c,d) + X[11] + 0x5a827999, 13) + e; c = RL(c, 10); + e = RL(e + f2(a,b,c) + X[ 8] + 0x5a827999, 12) + d; b = RL(b, 10); + + // right + ee = RL(ee + f4(aa,bb,cc) + X[ 6] + 0x5c4dd124, 9) + dd; bb = RL(bb, 10); + dd = RL(dd + f4(ee,aa,bb) + X[11] + 0x5c4dd124, 13) + cc; aa = RL(aa, 10); + cc = RL(cc + f4(dd,ee,aa) + X[ 3] + 0x5c4dd124, 15) + bb; ee = RL(ee, 10); + bb = RL(bb + f4(cc,dd,ee) + X[ 7] + 0x5c4dd124, 7) + aa; dd = RL(dd, 10); + aa = RL(aa + f4(bb,cc,dd) + X[ 0] + 0x5c4dd124, 12) + ee; cc = RL(cc, 10); + ee = RL(ee + f4(aa,bb,cc) + X[13] + 0x5c4dd124, 8) + dd; bb = RL(bb, 10); + dd = RL(dd + f4(ee,aa,bb) + X[ 5] + 0x5c4dd124, 9) + cc; aa = RL(aa, 10); + cc = RL(cc + f4(dd,ee,aa) + X[10] + 0x5c4dd124, 11) + bb; ee = RL(ee, 10); + bb = RL(bb + f4(cc,dd,ee) + X[14] + 0x5c4dd124, 7) + aa; dd = RL(dd, 10); + aa = RL(aa + f4(bb,cc,dd) + X[15] + 0x5c4dd124, 7) + ee; cc = RL(cc, 10); + ee = RL(ee + f4(aa,bb,cc) + X[ 8] + 0x5c4dd124, 12) + dd; bb = RL(bb, 10); + dd = RL(dd + f4(ee,aa,bb) + X[12] + 0x5c4dd124, 7) + cc; aa = RL(aa, 10); + cc = RL(cc + f4(dd,ee,aa) + X[ 4] + 0x5c4dd124, 6) + bb; ee = RL(ee, 10); + bb = RL(bb + f4(cc,dd,ee) + X[ 9] + 0x5c4dd124, 15) + aa; dd = RL(dd, 10); + aa = RL(aa + f4(bb,cc,dd) + X[ 1] + 0x5c4dd124, 13) + ee; cc = RL(cc, 10); + ee = RL(ee + f4(aa,bb,cc) + X[ 2] + 0x5c4dd124, 11) + dd; bb = RL(bb, 10); + + // + // Rounds 32-47 + // + // left + d = RL(d + f3(e,a,b) + X[ 3] + 0x6ed9eba1, 11) + c; a = RL(a, 10); + c = RL(c + f3(d,e,a) + X[10] + 0x6ed9eba1, 13) + b; e = RL(e, 10); + b = RL(b + f3(c,d,e) + X[14] + 0x6ed9eba1, 6) + a; d = RL(d, 10); + a = RL(a + f3(b,c,d) + X[ 4] + 0x6ed9eba1, 7) + e; c = RL(c, 10); + e = RL(e + f3(a,b,c) + X[ 9] + 0x6ed9eba1, 14) + d; b = RL(b, 10); + d = RL(d + f3(e,a,b) + X[15] + 0x6ed9eba1, 9) + c; a = RL(a, 10); + c = RL(c + f3(d,e,a) + X[ 8] + 0x6ed9eba1, 13) + b; e = RL(e, 10); + b = RL(b + f3(c,d,e) + X[ 1] + 0x6ed9eba1, 15) + a; d = RL(d, 10); + a = RL(a + f3(b,c,d) + X[ 2] + 0x6ed9eba1, 14) + e; c = RL(c, 10); + e = RL(e + f3(a,b,c) + X[ 7] + 0x6ed9eba1, 8) + d; b = RL(b, 10); + d = RL(d + f3(e,a,b) + X[ 0] + 0x6ed9eba1, 13) + c; a = RL(a, 10); + c = RL(c + f3(d,e,a) + X[ 6] + 0x6ed9eba1, 6) + b; e = RL(e, 10); + b = RL(b + f3(c,d,e) + X[13] + 0x6ed9eba1, 5) + a; d = RL(d, 10); + a = RL(a + f3(b,c,d) + X[11] + 0x6ed9eba1, 12) + e; c = RL(c, 10); + e = RL(e + f3(a,b,c) + X[ 5] + 0x6ed9eba1, 7) + d; b = RL(b, 10); + d = RL(d + f3(e,a,b) + X[12] + 0x6ed9eba1, 5) + c; a = RL(a, 10); + + // right + dd = RL(dd + f3(ee,aa,bb) + X[15] + 0x6d703ef3, 9) + cc; aa = RL(aa, 10); + cc = RL(cc + f3(dd,ee,aa) + X[ 5] + 0x6d703ef3, 7) + bb; ee = RL(ee, 10); + bb = RL(bb + f3(cc,dd,ee) + X[ 1] + 0x6d703ef3, 15) + aa; dd = RL(dd, 10); + aa = RL(aa + f3(bb,cc,dd) + X[ 3] + 0x6d703ef3, 11) + ee; cc = RL(cc, 10); + ee = RL(ee + f3(aa,bb,cc) + X[ 7] + 0x6d703ef3, 8) + dd; bb = RL(bb, 10); + dd = RL(dd + f3(ee,aa,bb) + X[14] + 0x6d703ef3, 6) + cc; aa = RL(aa, 10); + cc = RL(cc + f3(dd,ee,aa) + X[ 6] + 0x6d703ef3, 6) + bb; ee = RL(ee, 10); + bb = RL(bb + f3(cc,dd,ee) + X[ 9] + 0x6d703ef3, 14) + aa; dd = RL(dd, 10); + aa = RL(aa + f3(bb,cc,dd) + X[11] + 0x6d703ef3, 12) + ee; cc = RL(cc, 10); + ee = RL(ee + f3(aa,bb,cc) + X[ 8] + 0x6d703ef3, 13) + dd; bb = RL(bb, 10); + dd = RL(dd + f3(ee,aa,bb) + X[12] + 0x6d703ef3, 5) + cc; aa = RL(aa, 10); + cc = RL(cc + f3(dd,ee,aa) + X[ 2] + 0x6d703ef3, 14) + bb; ee = RL(ee, 10); + bb = RL(bb + f3(cc,dd,ee) + X[10] + 0x6d703ef3, 13) + aa; dd = RL(dd, 10); + aa = RL(aa + f3(bb,cc,dd) + X[ 0] + 0x6d703ef3, 13) + ee; cc = RL(cc, 10); + ee = RL(ee + f3(aa,bb,cc) + X[ 4] + 0x6d703ef3, 7) + dd; bb = RL(bb, 10); + dd = RL(dd + f3(ee,aa,bb) + X[13] + 0x6d703ef3, 5) + cc; aa = RL(aa, 10); + + // + // Rounds 48-63 + // + // left + c = RL(c + f4(d,e,a) + X[ 1] + 0x8f1bbcdc, 11) + b; e = RL(e, 10); + b = RL(b + f4(c,d,e) + X[ 9] + 0x8f1bbcdc, 12) + a; d = RL(d, 10); + a = RL(a + f4(b,c,d) + X[11] + 0x8f1bbcdc, 14) + e; c = RL(c, 10); + e = RL(e + f4(a,b,c) + X[10] + 0x8f1bbcdc, 15) + d; b = RL(b, 10); + d = RL(d + f4(e,a,b) + X[ 0] + 0x8f1bbcdc, 14) + c; a = RL(a, 10); + c = RL(c + f4(d,e,a) + X[ 8] + 0x8f1bbcdc, 15) + b; e = RL(e, 10); + b = RL(b + f4(c,d,e) + X[12] + 0x8f1bbcdc, 9) + a; d = RL(d, 10); + a = RL(a + f4(b,c,d) + X[ 4] + 0x8f1bbcdc, 8) + e; c = RL(c, 10); + e = RL(e + f4(a,b,c) + X[13] + 0x8f1bbcdc, 9) + d; b = RL(b, 10); + d = RL(d + f4(e,a,b) + X[ 3] + 0x8f1bbcdc, 14) + c; a = RL(a, 10); + c = RL(c + f4(d,e,a) + X[ 7] + 0x8f1bbcdc, 5) + b; e = RL(e, 10); + b = RL(b + f4(c,d,e) + X[15] + 0x8f1bbcdc, 6) + a; d = RL(d, 10); + a = RL(a + f4(b,c,d) + X[14] + 0x8f1bbcdc, 8) + e; c = RL(c, 10); + e = RL(e + f4(a,b,c) + X[ 5] + 0x8f1bbcdc, 6) + d; b = RL(b, 10); + d = RL(d + f4(e,a,b) + X[ 6] + 0x8f1bbcdc, 5) + c; a = RL(a, 10); + c = RL(c + f4(d,e,a) + X[ 2] + 0x8f1bbcdc, 12) + b; e = RL(e, 10); + + // right + cc = RL(cc + f2(dd,ee,aa) + X[ 8] + 0x7a6d76e9, 15) + bb; ee = RL(ee, 10); + bb = RL(bb + f2(cc,dd,ee) + X[ 6] + 0x7a6d76e9, 5) + aa; dd = RL(dd, 10); + aa = RL(aa + f2(bb,cc,dd) + X[ 4] + 0x7a6d76e9, 8) + ee; cc = RL(cc, 10); + ee = RL(ee + f2(aa,bb,cc) + X[ 1] + 0x7a6d76e9, 11) + dd; bb = RL(bb, 10); + dd = RL(dd + f2(ee,aa,bb) + X[ 3] + 0x7a6d76e9, 14) + cc; aa = RL(aa, 10); + cc = RL(cc + f2(dd,ee,aa) + X[11] + 0x7a6d76e9, 14) + bb; ee = RL(ee, 10); + bb = RL(bb + f2(cc,dd,ee) + X[15] + 0x7a6d76e9, 6) + aa; dd = RL(dd, 10); + aa = RL(aa + f2(bb,cc,dd) + X[ 0] + 0x7a6d76e9, 14) + ee; cc = RL(cc, 10); + ee = RL(ee + f2(aa,bb,cc) + X[ 5] + 0x7a6d76e9, 6) + dd; bb = RL(bb, 10); + dd = RL(dd + f2(ee,aa,bb) + X[12] + 0x7a6d76e9, 9) + cc; aa = RL(aa, 10); + cc = RL(cc + f2(dd,ee,aa) + X[ 2] + 0x7a6d76e9, 12) + bb; ee = RL(ee, 10); + bb = RL(bb + f2(cc,dd,ee) + X[13] + 0x7a6d76e9, 9) + aa; dd = RL(dd, 10); + aa = RL(aa + f2(bb,cc,dd) + X[ 9] + 0x7a6d76e9, 12) + ee; cc = RL(cc, 10); + ee = RL(ee + f2(aa,bb,cc) + X[ 7] + 0x7a6d76e9, 5) + dd; bb = RL(bb, 10); + dd = RL(dd + f2(ee,aa,bb) + X[10] + 0x7a6d76e9, 15) + cc; aa = RL(aa, 10); + cc = RL(cc + f2(dd,ee,aa) + X[14] + 0x7a6d76e9, 8) + bb; ee = RL(ee, 10); + + // + // Rounds 64-79 + // + // left + b = RL(b + f5(c,d,e) + X[ 4] + 0xa953fd4e, 9) + a; d = RL(d, 10); + a = RL(a + f5(b,c,d) + X[ 0] + 0xa953fd4e, 15) + e; c = RL(c, 10); + e = RL(e + f5(a,b,c) + X[ 5] + 0xa953fd4e, 5) + d; b = RL(b, 10); + d = RL(d + f5(e,a,b) + X[ 9] + 0xa953fd4e, 11) + c; a = RL(a, 10); + c = RL(c + f5(d,e,a) + X[ 7] + 0xa953fd4e, 6) + b; e = RL(e, 10); + b = RL(b + f5(c,d,e) + X[12] + 0xa953fd4e, 8) + a; d = RL(d, 10); + a = RL(a + f5(b,c,d) + X[ 2] + 0xa953fd4e, 13) + e; c = RL(c, 10); + e = RL(e + f5(a,b,c) + X[10] + 0xa953fd4e, 12) + d; b = RL(b, 10); + d = RL(d + f5(e,a,b) + X[14] + 0xa953fd4e, 5) + c; a = RL(a, 10); + c = RL(c + f5(d,e,a) + X[ 1] + 0xa953fd4e, 12) + b; e = RL(e, 10); + b = RL(b + f5(c,d,e) + X[ 3] + 0xa953fd4e, 13) + a; d = RL(d, 10); + a = RL(a + f5(b,c,d) + X[ 8] + 0xa953fd4e, 14) + e; c = RL(c, 10); + e = RL(e + f5(a,b,c) + X[11] + 0xa953fd4e, 11) + d; b = RL(b, 10); + d = RL(d + f5(e,a,b) + X[ 6] + 0xa953fd4e, 8) + c; a = RL(a, 10); + c = RL(c + f5(d,e,a) + X[15] + 0xa953fd4e, 5) + b; e = RL(e, 10); + b = RL(b + f5(c,d,e) + X[13] + 0xa953fd4e, 6) + a; d = RL(d, 10); + + // right + bb = RL(bb + f1(cc,dd,ee) + X[12], 8) + aa; dd = RL(dd, 10); + aa = RL(aa + f1(bb,cc,dd) + X[15], 5) + ee; cc = RL(cc, 10); + ee = RL(ee + f1(aa,bb,cc) + X[10], 12) + dd; bb = RL(bb, 10); + dd = RL(dd + f1(ee,aa,bb) + X[ 4], 9) + cc; aa = RL(aa, 10); + cc = RL(cc + f1(dd,ee,aa) + X[ 1], 12) + bb; ee = RL(ee, 10); + bb = RL(bb + f1(cc,dd,ee) + X[ 5], 5) + aa; dd = RL(dd, 10); + aa = RL(aa + f1(bb,cc,dd) + X[ 8], 14) + ee; cc = RL(cc, 10); + ee = RL(ee + f1(aa,bb,cc) + X[ 7], 6) + dd; bb = RL(bb, 10); + dd = RL(dd + f1(ee,aa,bb) + X[ 6], 8) + cc; aa = RL(aa, 10); + cc = RL(cc + f1(dd,ee,aa) + X[ 2], 13) + bb; ee = RL(ee, 10); + bb = RL(bb + f1(cc,dd,ee) + X[13], 6) + aa; dd = RL(dd, 10); + aa = RL(aa + f1(bb,cc,dd) + X[14], 5) + ee; cc = RL(cc, 10); + ee = RL(ee + f1(aa,bb,cc) + X[ 0], 15) + dd; bb = RL(bb, 10); + dd = RL(dd + f1(ee,aa,bb) + X[ 3], 13) + cc; aa = RL(aa, 10); + cc = RL(cc + f1(dd,ee,aa) + X[ 9], 11) + bb; ee = RL(ee, 10); + bb = RL(bb + f1(cc,dd,ee) + X[11], 11) + aa; dd = RL(dd, 10); + + dd += c + H1; + H1 = H2 + d + ee; + H2 = H3 + e + aa; + H3 = H4 + a + bb; + H4 = H0 + b + cc; + H0 = dd; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/SHA1Digest.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/SHA1Digest.java new file mode 100644 index 00000000..7f8d30a0 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/SHA1Digest.java @@ -0,0 +1,290 @@ +package org.bouncycastle.crypto.digests; + +import org.bouncycastle.crypto.util.Pack; + +/** + * implementation of SHA-1 as outlined in "Handbook of Applied Cryptography", pages 346 - 349. + * + * It is interesting to ponder why the, apart from the extra IV, the other difference here from MD5 + * is the "endienness" of the word processing! + */ +public class SHA1Digest + extends GeneralDigest +{ + private static final int DIGEST_LENGTH = 20; + + private int H1, H2, H3, H4, H5; + + private int[] X = new int[80]; + private int xOff; + + /** + * Standard constructor + */ + public SHA1Digest() + { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public SHA1Digest(SHA1Digest t) + { + super(t); + + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + public String getAlgorithmName() + { + return "SHA-1"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + // Note: Inlined for performance +// X[xOff] = Pack.bigEndianToInt(in, inOff); + int n = in[ inOff] << 24; + n |= (in[++inOff] & 0xff) << 16; + n |= (in[++inOff] & 0xff) << 8; + n |= (in[++inOff] & 0xff); + X[xOff] = n; + + if (++xOff == 16) + { + processBlock(); + } + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int)(bitLength >>> 32); + X[15] = (int)(bitLength & 0xffffffff); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + Pack.intToBigEndian(H1, out, outOff); + Pack.intToBigEndian(H2, out, outOff + 4); + Pack.intToBigEndian(H3, out, outOff + 8); + Pack.intToBigEndian(H4, out, outOff + 12); + Pack.intToBigEndian(H5, out, outOff + 16); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables + */ + public void reset() + { + super.reset(); + + H1 = 0x67452301; + H2 = 0xefcdab89; + H3 = 0x98badcfe; + H4 = 0x10325476; + H5 = 0xc3d2e1f0; + + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + // + // Additive constants + // + private static final int Y1 = 0x5a827999; + private static final int Y2 = 0x6ed9eba1; + private static final int Y3 = 0x8f1bbcdc; + private static final int Y4 = 0xca62c1d6; + + private int f( + int u, + int v, + int w) + { + return ((u & v) | ((~u) & w)); + } + + private int h( + int u, + int v, + int w) + { + return (u ^ v ^ w); + } + + private int g( + int u, + int v, + int w) + { + return ((u & v) | (u & w) | (v & w)); + } + + protected void processBlock() + { + // + // expand 16 word block into 80 word block. + // + for (int i = 16; i < 80; i++) + { + int t = X[i - 3] ^ X[i - 8] ^ X[i - 14] ^ X[i - 16]; + X[i] = t << 1 | t >>> 31; + } + + // + // set up working variables. + // + int A = H1; + int B = H2; + int C = H3; + int D = H4; + int E = H5; + + // + // round 1 + // + int idx = 0; + + for (int j = 0; j < 4; j++) + { + // E = rotateLeft(A, 5) + f(B, C, D) + E + X[idx++] + Y1 + // B = rotateLeft(B, 30) + E += (A << 5 | A >>> 27) + f(B, C, D) + X[idx++] + Y1; + B = B << 30 | B >>> 2; + + D += (E << 5 | E >>> 27) + f(A, B, C) + X[idx++] + Y1; + A = A << 30 | A >>> 2; + + C += (D << 5 | D >>> 27) + f(E, A, B) + X[idx++] + Y1; + E = E << 30 | E >>> 2; + + B += (C << 5 | C >>> 27) + f(D, E, A) + X[idx++] + Y1; + D = D << 30 | D >>> 2; + + A += (B << 5 | B >>> 27) + f(C, D, E) + X[idx++] + Y1; + C = C << 30 | C >>> 2; + } + + // + // round 2 + // + for (int j = 0; j < 4; j++) + { + // E = rotateLeft(A, 5) + h(B, C, D) + E + X[idx++] + Y2 + // B = rotateLeft(B, 30) + E += (A << 5 | A >>> 27) + h(B, C, D) + X[idx++] + Y2; + B = B << 30 | B >>> 2; + + D += (E << 5 | E >>> 27) + h(A, B, C) + X[idx++] + Y2; + A = A << 30 | A >>> 2; + + C += (D << 5 | D >>> 27) + h(E, A, B) + X[idx++] + Y2; + E = E << 30 | E >>> 2; + + B += (C << 5 | C >>> 27) + h(D, E, A) + X[idx++] + Y2; + D = D << 30 | D >>> 2; + + A += (B << 5 | B >>> 27) + h(C, D, E) + X[idx++] + Y2; + C = C << 30 | C >>> 2; + } + + // + // round 3 + // + for (int j = 0; j < 4; j++) + { + // E = rotateLeft(A, 5) + g(B, C, D) + E + X[idx++] + Y3 + // B = rotateLeft(B, 30) + E += (A << 5 | A >>> 27) + g(B, C, D) + X[idx++] + Y3; + B = B << 30 | B >>> 2; + + D += (E << 5 | E >>> 27) + g(A, B, C) + X[idx++] + Y3; + A = A << 30 | A >>> 2; + + C += (D << 5 | D >>> 27) + g(E, A, B) + X[idx++] + Y3; + E = E << 30 | E >>> 2; + + B += (C << 5 | C >>> 27) + g(D, E, A) + X[idx++] + Y3; + D = D << 30 | D >>> 2; + + A += (B << 5 | B >>> 27) + g(C, D, E) + X[idx++] + Y3; + C = C << 30 | C >>> 2; + } + + // + // round 4 + // + for (int j = 0; j <= 3; j++) + { + // E = rotateLeft(A, 5) + h(B, C, D) + E + X[idx++] + Y4 + // B = rotateLeft(B, 30) + E += (A << 5 | A >>> 27) + h(B, C, D) + X[idx++] + Y4; + B = B << 30 | B >>> 2; + + D += (E << 5 | E >>> 27) + h(A, B, C) + X[idx++] + Y4; + A = A << 30 | A >>> 2; + + C += (D << 5 | D >>> 27) + h(E, A, B) + X[idx++] + Y4; + E = E << 30 | E >>> 2; + + B += (C << 5 | C >>> 27) + h(D, E, A) + X[idx++] + Y4; + D = D << 30 | D >>> 2; + + A += (B << 5 | B >>> 27) + h(C, D, E) + X[idx++] + Y4; + C = C << 30 | C >>> 2; + } + + + H1 += A; + H2 += B; + H3 += C; + H4 += D; + H5 += E; + + // + // reset start of the buffer. + // + xOff = 0; + for (int i = 0; i < 16; i++) + { + X[i] = 0; + } + } +} + + + + diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/SHA256Digest.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/SHA256Digest.java new file mode 100644 index 00000000..abd9c1bb --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/SHA256Digest.java @@ -0,0 +1,295 @@ +package org.bouncycastle.crypto.digests; + + +import org.bouncycastle.crypto.digests.GeneralDigest; +import org.bouncycastle.crypto.util.Pack; + + +/** + * FIPS 180-2 implementation of SHA-256. + * + *

+ *         block  word  digest
+ * SHA-1   512    32    160
+ * SHA-256 512    32    256
+ * SHA-384 1024   64    384
+ * SHA-512 1024   64    512
+ * 
+ */ +public class SHA256Digest + extends GeneralDigest +{ + private static final int DIGEST_LENGTH = 32; + + private int H1, H2, H3, H4, H5, H6, H7, H8; + + private int[] X = new int[64]; + private int xOff; + + /** + * Standard constructor + */ + public SHA256Digest() + { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public SHA256Digest(SHA256Digest t) + { + super(t); + + H1 = t.H1; + H2 = t.H2; + H3 = t.H3; + H4 = t.H4; + H5 = t.H5; + H6 = t.H6; + H7 = t.H7; + H8 = t.H8; + + System.arraycopy(t.X, 0, X, 0, t.X.length); + xOff = t.xOff; + } + + public String getAlgorithmName() + { + return "SHA-256"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + protected void processWord( + byte[] in, + int inOff) + { + // Note: Inlined for performance +// X[xOff] = Pack.bigEndianToInt(in, inOff); + int n = in[inOff] << 24; + n |= (in[++inOff] & 0xff) << 16; + n |= (in[++inOff] & 0xff) << 8; + n |= (in[++inOff] & 0xff); + X[xOff] = n; + + if (++xOff == 16) + { + processBlock(); + } + } + + protected void processLength( + long bitLength) + { + if (xOff > 14) + { + processBlock(); + } + + X[14] = (int)(bitLength >>> 32); + X[15] = (int)(bitLength & 0xffffffff); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + Pack.intToBigEndian(H1, out, outOff); + Pack.intToBigEndian(H2, out, outOff + 4); + Pack.intToBigEndian(H3, out, outOff + 8); + Pack.intToBigEndian(H4, out, outOff + 12); + Pack.intToBigEndian(H5, out, outOff + 16); + Pack.intToBigEndian(H6, out, outOff + 20); + Pack.intToBigEndian(H7, out, outOff + 24); + Pack.intToBigEndian(H8, out, outOff + 28); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables + */ + public void reset() + { + super.reset(); + + /* SHA-256 initial hash value + * The first 32 bits of the fractional parts of the square roots + * of the first eight prime numbers + */ + + H1 = 0x6a09e667; + H2 = 0xbb67ae85; + H3 = 0x3c6ef372; + H4 = 0xa54ff53a; + H5 = 0x510e527f; + H6 = 0x9b05688c; + H7 = 0x1f83d9ab; + H8 = 0x5be0cd19; + + xOff = 0; + for (int i = 0; i != X.length; i++) + { + X[i] = 0; + } + } + + protected void processBlock() + { + // + // expand 16 word block into 64 word blocks. + // + for (int t = 16; t <= 63; t++) + { + X[t] = Theta1(X[t - 2]) + X[t - 7] + Theta0(X[t - 15]) + X[t - 16]; + } + + // + // set up working variables. + // + int a = H1; + int b = H2; + int c = H3; + int d = H4; + int e = H5; + int f = H6; + int g = H7; + int h = H8; + + int t = 0; + for(int i = 0; i < 8; i ++) + { + // t = 8 * i + h += Sum1(e) + Ch(e, f, g) + K[t] + X[t]; + d += h; + h += Sum0(a) + Maj(a, b, c); + ++t; + + // t = 8 * i + 1 + g += Sum1(d) + Ch(d, e, f) + K[t] + X[t]; + c += g; + g += Sum0(h) + Maj(h, a, b); + ++t; + + // t = 8 * i + 2 + f += Sum1(c) + Ch(c, d, e) + K[t] + X[t]; + b += f; + f += Sum0(g) + Maj(g, h, a); + ++t; + + // t = 8 * i + 3 + e += Sum1(b) + Ch(b, c, d) + K[t] + X[t]; + a += e; + e += Sum0(f) + Maj(f, g, h); + ++t; + + // t = 8 * i + 4 + d += Sum1(a) + Ch(a, b, c) + K[t] + X[t]; + h += d; + d += Sum0(e) + Maj(e, f, g); + ++t; + + // t = 8 * i + 5 + c += Sum1(h) + Ch(h, a, b) + K[t] + X[t]; + g += c; + c += Sum0(d) + Maj(d, e, f); + ++t; + + // t = 8 * i + 6 + b += Sum1(g) + Ch(g, h, a) + K[t] + X[t]; + f += b; + b += Sum0(c) + Maj(c, d, e); + ++t; + + // t = 8 * i + 7 + a += Sum1(f) + Ch(f, g, h) + K[t] + X[t]; + e += a; + a += Sum0(b) + Maj(b, c, d); + ++t; + } + + H1 += a; + H2 += b; + H3 += c; + H4 += d; + H5 += e; + H6 += f; + H7 += g; + H8 += h; + + // + // reset the offset and clean out the word buffer. + // + xOff = 0; + for (int i = 0; i < 16; i++) + { + X[i] = 0; + } + } + + /* SHA-256 functions */ + private int Ch( + int x, + int y, + int z) + { + return (x & y) ^ ((~x) & z); + } + + private int Maj( + int x, + int y, + int z) + { + return (x & y) ^ (x & z) ^ (y & z); + } + + private int Sum0( + int x) + { + return ((x >>> 2) | (x << 30)) ^ ((x >>> 13) | (x << 19)) ^ ((x >>> 22) | (x << 10)); + } + + private int Sum1( + int x) + { + return ((x >>> 6) | (x << 26)) ^ ((x >>> 11) | (x << 21)) ^ ((x >>> 25) | (x << 7)); + } + + private int Theta0( + int x) + { + return ((x >>> 7) | (x << 25)) ^ ((x >>> 18) | (x << 14)) ^ (x >>> 3); + } + + private int Theta1( + int x) + { + return ((x >>> 17) | (x << 15)) ^ ((x >>> 19) | (x << 13)) ^ (x >>> 10); + } + + /* SHA-256 Constants + * (represent the first 32 bits of the fractional parts of the + * cube roots of the first sixty-four prime numbers) + */ + static final int K[] = { + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + }; +} + diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/TigerDigest.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/TigerDigest.java new file mode 100644 index 00000000..0062ea92 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/digests/TigerDigest.java @@ -0,0 +1,866 @@ +package org.bouncycastle.crypto.digests; + +import org.bouncycastle.crypto.ExtendedDigest; + +/** + * implementation of Tiger based on: + * + * http://www.cs.technion.ac.il/~biham/Reports/Tiger + */ +public class TigerDigest + implements ExtendedDigest +{ + private static final int BYTE_LENGTH = 64; + + /* + * S-Boxes. + */ + private static final long[] t1 = { + 0x02AAB17CF7E90C5EL /* 0 */, 0xAC424B03E243A8ECL /* 1 */, + 0x72CD5BE30DD5FCD3L /* 2 */, 0x6D019B93F6F97F3AL /* 3 */, + 0xCD9978FFD21F9193L /* 4 */, 0x7573A1C9708029E2L /* 5 */, + 0xB164326B922A83C3L /* 6 */, 0x46883EEE04915870L /* 7 */, + 0xEAACE3057103ECE6L /* 8 */, 0xC54169B808A3535CL /* 9 */, + 0x4CE754918DDEC47CL /* 10 */, 0x0AA2F4DFDC0DF40CL /* 11 */, + 0x10B76F18A74DBEFAL /* 12 */, 0xC6CCB6235AD1AB6AL /* 13 */, + 0x13726121572FE2FFL /* 14 */, 0x1A488C6F199D921EL /* 15 */, + 0x4BC9F9F4DA0007CAL /* 16 */, 0x26F5E6F6E85241C7L /* 17 */, + 0x859079DBEA5947B6L /* 18 */, 0x4F1885C5C99E8C92L /* 19 */, + 0xD78E761EA96F864BL /* 20 */, 0x8E36428C52B5C17DL /* 21 */, + 0x69CF6827373063C1L /* 22 */, 0xB607C93D9BB4C56EL /* 23 */, + 0x7D820E760E76B5EAL /* 24 */, 0x645C9CC6F07FDC42L /* 25 */, + 0xBF38A078243342E0L /* 26 */, 0x5F6B343C9D2E7D04L /* 27 */, + 0xF2C28AEB600B0EC6L /* 28 */, 0x6C0ED85F7254BCACL /* 29 */, + 0x71592281A4DB4FE5L /* 30 */, 0x1967FA69CE0FED9FL /* 31 */, + 0xFD5293F8B96545DBL /* 32 */, 0xC879E9D7F2A7600BL /* 33 */, + 0x860248920193194EL /* 34 */, 0xA4F9533B2D9CC0B3L /* 35 */, + 0x9053836C15957613L /* 36 */, 0xDB6DCF8AFC357BF1L /* 37 */, + 0x18BEEA7A7A370F57L /* 38 */, 0x037117CA50B99066L /* 39 */, + 0x6AB30A9774424A35L /* 40 */, 0xF4E92F02E325249BL /* 41 */, + 0x7739DB07061CCAE1L /* 42 */, 0xD8F3B49CECA42A05L /* 43 */, + 0xBD56BE3F51382F73L /* 44 */, 0x45FAED5843B0BB28L /* 45 */, + 0x1C813D5C11BF1F83L /* 46 */, 0x8AF0E4B6D75FA169L /* 47 */, + 0x33EE18A487AD9999L /* 48 */, 0x3C26E8EAB1C94410L /* 49 */, + 0xB510102BC0A822F9L /* 50 */, 0x141EEF310CE6123BL /* 51 */, + 0xFC65B90059DDB154L /* 52 */, 0xE0158640C5E0E607L /* 53 */, + 0x884E079826C3A3CFL /* 54 */, 0x930D0D9523C535FDL /* 55 */, + 0x35638D754E9A2B00L /* 56 */, 0x4085FCCF40469DD5L /* 57 */, + 0xC4B17AD28BE23A4CL /* 58 */, 0xCAB2F0FC6A3E6A2EL /* 59 */, + 0x2860971A6B943FCDL /* 60 */, 0x3DDE6EE212E30446L /* 61 */, + 0x6222F32AE01765AEL /* 62 */, 0x5D550BB5478308FEL /* 63 */, + 0xA9EFA98DA0EDA22AL /* 64 */, 0xC351A71686C40DA7L /* 65 */, + 0x1105586D9C867C84L /* 66 */, 0xDCFFEE85FDA22853L /* 67 */, + 0xCCFBD0262C5EEF76L /* 68 */, 0xBAF294CB8990D201L /* 69 */, + 0xE69464F52AFAD975L /* 70 */, 0x94B013AFDF133E14L /* 71 */, + 0x06A7D1A32823C958L /* 72 */, 0x6F95FE5130F61119L /* 73 */, + 0xD92AB34E462C06C0L /* 74 */, 0xED7BDE33887C71D2L /* 75 */, + 0x79746D6E6518393EL /* 76 */, 0x5BA419385D713329L /* 77 */, + 0x7C1BA6B948A97564L /* 78 */, 0x31987C197BFDAC67L /* 79 */, + 0xDE6C23C44B053D02L /* 80 */, 0x581C49FED002D64DL /* 81 */, + 0xDD474D6338261571L /* 82 */, 0xAA4546C3E473D062L /* 83 */, + 0x928FCE349455F860L /* 84 */, 0x48161BBACAAB94D9L /* 85 */, + 0x63912430770E6F68L /* 86 */, 0x6EC8A5E602C6641CL /* 87 */, + 0x87282515337DDD2BL /* 88 */, 0x2CDA6B42034B701BL /* 89 */, + 0xB03D37C181CB096DL /* 90 */, 0xE108438266C71C6FL /* 91 */, + 0x2B3180C7EB51B255L /* 92 */, 0xDF92B82F96C08BBCL /* 93 */, + 0x5C68C8C0A632F3BAL /* 94 */, 0x5504CC861C3D0556L /* 95 */, + 0xABBFA4E55FB26B8FL /* 96 */, 0x41848B0AB3BACEB4L /* 97 */, + 0xB334A273AA445D32L /* 98 */, 0xBCA696F0A85AD881L /* 99 */, + 0x24F6EC65B528D56CL /* 100 */, 0x0CE1512E90F4524AL /* 101 */, + 0x4E9DD79D5506D35AL /* 102 */, 0x258905FAC6CE9779L /* 103 */, + 0x2019295B3E109B33L /* 104 */, 0xF8A9478B73A054CCL /* 105 */, + 0x2924F2F934417EB0L /* 106 */, 0x3993357D536D1BC4L /* 107 */, + 0x38A81AC21DB6FF8BL /* 108 */, 0x47C4FBF17D6016BFL /* 109 */, + 0x1E0FAADD7667E3F5L /* 110 */, 0x7ABCFF62938BEB96L /* 111 */, + 0xA78DAD948FC179C9L /* 112 */, 0x8F1F98B72911E50DL /* 113 */, + 0x61E48EAE27121A91L /* 114 */, 0x4D62F7AD31859808L /* 115 */, + 0xECEBA345EF5CEAEBL /* 116 */, 0xF5CEB25EBC9684CEL /* 117 */, + 0xF633E20CB7F76221L /* 118 */, 0xA32CDF06AB8293E4L /* 119 */, + 0x985A202CA5EE2CA4L /* 120 */, 0xCF0B8447CC8A8FB1L /* 121 */, + 0x9F765244979859A3L /* 122 */, 0xA8D516B1A1240017L /* 123 */, + 0x0BD7BA3EBB5DC726L /* 124 */, 0xE54BCA55B86ADB39L /* 125 */, + 0x1D7A3AFD6C478063L /* 126 */, 0x519EC608E7669EDDL /* 127 */, + 0x0E5715A2D149AA23L /* 128 */, 0x177D4571848FF194L /* 129 */, + 0xEEB55F3241014C22L /* 130 */, 0x0F5E5CA13A6E2EC2L /* 131 */, + 0x8029927B75F5C361L /* 132 */, 0xAD139FABC3D6E436L /* 133 */, + 0x0D5DF1A94CCF402FL /* 134 */, 0x3E8BD948BEA5DFC8L /* 135 */, + 0xA5A0D357BD3FF77EL /* 136 */, 0xA2D12E251F74F645L /* 137 */, + 0x66FD9E525E81A082L /* 138 */, 0x2E0C90CE7F687A49L /* 139 */, + 0xC2E8BCBEBA973BC5L /* 140 */, 0x000001BCE509745FL /* 141 */, + 0x423777BBE6DAB3D6L /* 142 */, 0xD1661C7EAEF06EB5L /* 143 */, + 0xA1781F354DAACFD8L /* 144 */, 0x2D11284A2B16AFFCL /* 145 */, + 0xF1FC4F67FA891D1FL /* 146 */, 0x73ECC25DCB920ADAL /* 147 */, + 0xAE610C22C2A12651L /* 148 */, 0x96E0A810D356B78AL /* 149 */, + 0x5A9A381F2FE7870FL /* 150 */, 0xD5AD62EDE94E5530L /* 151 */, + 0xD225E5E8368D1427L /* 152 */, 0x65977B70C7AF4631L /* 153 */, + 0x99F889B2DE39D74FL /* 154 */, 0x233F30BF54E1D143L /* 155 */, + 0x9A9675D3D9A63C97L /* 156 */, 0x5470554FF334F9A8L /* 157 */, + 0x166ACB744A4F5688L /* 158 */, 0x70C74CAAB2E4AEADL /* 159 */, + 0xF0D091646F294D12L /* 160 */, 0x57B82A89684031D1L /* 161 */, + 0xEFD95A5A61BE0B6BL /* 162 */, 0x2FBD12E969F2F29AL /* 163 */, + 0x9BD37013FEFF9FE8L /* 164 */, 0x3F9B0404D6085A06L /* 165 */, + 0x4940C1F3166CFE15L /* 166 */, 0x09542C4DCDF3DEFBL /* 167 */, + 0xB4C5218385CD5CE3L /* 168 */, 0xC935B7DC4462A641L /* 169 */, + 0x3417F8A68ED3B63FL /* 170 */, 0xB80959295B215B40L /* 171 */, + 0xF99CDAEF3B8C8572L /* 172 */, 0x018C0614F8FCB95DL /* 173 */, + 0x1B14ACCD1A3ACDF3L /* 174 */, 0x84D471F200BB732DL /* 175 */, + 0xC1A3110E95E8DA16L /* 176 */, 0x430A7220BF1A82B8L /* 177 */, + 0xB77E090D39DF210EL /* 178 */, 0x5EF4BD9F3CD05E9DL /* 179 */, + 0x9D4FF6DA7E57A444L /* 180 */, 0xDA1D60E183D4A5F8L /* 181 */, + 0xB287C38417998E47L /* 182 */, 0xFE3EDC121BB31886L /* 183 */, + 0xC7FE3CCC980CCBEFL /* 184 */, 0xE46FB590189BFD03L /* 185 */, + 0x3732FD469A4C57DCL /* 186 */, 0x7EF700A07CF1AD65L /* 187 */, + 0x59C64468A31D8859L /* 188 */, 0x762FB0B4D45B61F6L /* 189 */, + 0x155BAED099047718L /* 190 */, 0x68755E4C3D50BAA6L /* 191 */, + 0xE9214E7F22D8B4DFL /* 192 */, 0x2ADDBF532EAC95F4L /* 193 */, + 0x32AE3909B4BD0109L /* 194 */, 0x834DF537B08E3450L /* 195 */, + 0xFA209DA84220728DL /* 196 */, 0x9E691D9B9EFE23F7L /* 197 */, + 0x0446D288C4AE8D7FL /* 198 */, 0x7B4CC524E169785BL /* 199 */, + 0x21D87F0135CA1385L /* 200 */, 0xCEBB400F137B8AA5L /* 201 */, + 0x272E2B66580796BEL /* 202 */, 0x3612264125C2B0DEL /* 203 */, + 0x057702BDAD1EFBB2L /* 204 */, 0xD4BABB8EACF84BE9L /* 205 */, + 0x91583139641BC67BL /* 206 */, 0x8BDC2DE08036E024L /* 207 */, + 0x603C8156F49F68EDL /* 208 */, 0xF7D236F7DBEF5111L /* 209 */, + 0x9727C4598AD21E80L /* 210 */, 0xA08A0896670A5FD7L /* 211 */, + 0xCB4A8F4309EBA9CBL /* 212 */, 0x81AF564B0F7036A1L /* 213 */, + 0xC0B99AA778199ABDL /* 214 */, 0x959F1EC83FC8E952L /* 215 */, + 0x8C505077794A81B9L /* 216 */, 0x3ACAAF8F056338F0L /* 217 */, + 0x07B43F50627A6778L /* 218 */, 0x4A44AB49F5ECCC77L /* 219 */, + 0x3BC3D6E4B679EE98L /* 220 */, 0x9CC0D4D1CF14108CL /* 221 */, + 0x4406C00B206BC8A0L /* 222 */, 0x82A18854C8D72D89L /* 223 */, + 0x67E366B35C3C432CL /* 224 */, 0xB923DD61102B37F2L /* 225 */, + 0x56AB2779D884271DL /* 226 */, 0xBE83E1B0FF1525AFL /* 227 */, + 0xFB7C65D4217E49A9L /* 228 */, 0x6BDBE0E76D48E7D4L /* 229 */, + 0x08DF828745D9179EL /* 230 */, 0x22EA6A9ADD53BD34L /* 231 */, + 0xE36E141C5622200AL /* 232 */, 0x7F805D1B8CB750EEL /* 233 */, + 0xAFE5C7A59F58E837L /* 234 */, 0xE27F996A4FB1C23CL /* 235 */, + 0xD3867DFB0775F0D0L /* 236 */, 0xD0E673DE6E88891AL /* 237 */, + 0x123AEB9EAFB86C25L /* 238 */, 0x30F1D5D5C145B895L /* 239 */, + 0xBB434A2DEE7269E7L /* 240 */, 0x78CB67ECF931FA38L /* 241 */, + 0xF33B0372323BBF9CL /* 242 */, 0x52D66336FB279C74L /* 243 */, + 0x505F33AC0AFB4EAAL /* 244 */, 0xE8A5CD99A2CCE187L /* 245 */, + 0x534974801E2D30BBL /* 246 */, 0x8D2D5711D5876D90L /* 247 */, + 0x1F1A412891BC038EL /* 248 */, 0xD6E2E71D82E56648L /* 249 */, + 0x74036C3A497732B7L /* 250 */, 0x89B67ED96361F5ABL /* 251 */, + 0xFFED95D8F1EA02A2L /* 252 */, 0xE72B3BD61464D43DL /* 253 */, + 0xA6300F170BDC4820L /* 254 */, 0xEBC18760ED78A77AL /* 255 */, + }; + + private static final long[] t2 = { + 0xE6A6BE5A05A12138L /* 256 */, 0xB5A122A5B4F87C98L /* 257 */, + 0x563C6089140B6990L /* 258 */, 0x4C46CB2E391F5DD5L /* 259 */, + 0xD932ADDBC9B79434L /* 260 */, 0x08EA70E42015AFF5L /* 261 */, + 0xD765A6673E478CF1L /* 262 */, 0xC4FB757EAB278D99L /* 263 */, + 0xDF11C6862D6E0692L /* 264 */, 0xDDEB84F10D7F3B16L /* 265 */, + 0x6F2EF604A665EA04L /* 266 */, 0x4A8E0F0FF0E0DFB3L /* 267 */, + 0xA5EDEEF83DBCBA51L /* 268 */, 0xFC4F0A2A0EA4371EL /* 269 */, + 0xE83E1DA85CB38429L /* 270 */, 0xDC8FF882BA1B1CE2L /* 271 */, + 0xCD45505E8353E80DL /* 272 */, 0x18D19A00D4DB0717L /* 273 */, + 0x34A0CFEDA5F38101L /* 274 */, 0x0BE77E518887CAF2L /* 275 */, + 0x1E341438B3C45136L /* 276 */, 0xE05797F49089CCF9L /* 277 */, + 0xFFD23F9DF2591D14L /* 278 */, 0x543DDA228595C5CDL /* 279 */, + 0x661F81FD99052A33L /* 280 */, 0x8736E641DB0F7B76L /* 281 */, + 0x15227725418E5307L /* 282 */, 0xE25F7F46162EB2FAL /* 283 */, + 0x48A8B2126C13D9FEL /* 284 */, 0xAFDC541792E76EEAL /* 285 */, + 0x03D912BFC6D1898FL /* 286 */, 0x31B1AAFA1B83F51BL /* 287 */, + 0xF1AC2796E42AB7D9L /* 288 */, 0x40A3A7D7FCD2EBACL /* 289 */, + 0x1056136D0AFBBCC5L /* 290 */, 0x7889E1DD9A6D0C85L /* 291 */, + 0xD33525782A7974AAL /* 292 */, 0xA7E25D09078AC09BL /* 293 */, + 0xBD4138B3EAC6EDD0L /* 294 */, 0x920ABFBE71EB9E70L /* 295 */, + 0xA2A5D0F54FC2625CL /* 296 */, 0xC054E36B0B1290A3L /* 297 */, + 0xF6DD59FF62FE932BL /* 298 */, 0x3537354511A8AC7DL /* 299 */, + 0xCA845E9172FADCD4L /* 300 */, 0x84F82B60329D20DCL /* 301 */, + 0x79C62CE1CD672F18L /* 302 */, 0x8B09A2ADD124642CL /* 303 */, + 0xD0C1E96A19D9E726L /* 304 */, 0x5A786A9B4BA9500CL /* 305 */, + 0x0E020336634C43F3L /* 306 */, 0xC17B474AEB66D822L /* 307 */, + 0x6A731AE3EC9BAAC2L /* 308 */, 0x8226667AE0840258L /* 309 */, + 0x67D4567691CAECA5L /* 310 */, 0x1D94155C4875ADB5L /* 311 */, + 0x6D00FD985B813FDFL /* 312 */, 0x51286EFCB774CD06L /* 313 */, + 0x5E8834471FA744AFL /* 314 */, 0xF72CA0AEE761AE2EL /* 315 */, + 0xBE40E4CDAEE8E09AL /* 316 */, 0xE9970BBB5118F665L /* 317 */, + 0x726E4BEB33DF1964L /* 318 */, 0x703B000729199762L /* 319 */, + 0x4631D816F5EF30A7L /* 320 */, 0xB880B5B51504A6BEL /* 321 */, + 0x641793C37ED84B6CL /* 322 */, 0x7B21ED77F6E97D96L /* 323 */, + 0x776306312EF96B73L /* 324 */, 0xAE528948E86FF3F4L /* 325 */, + 0x53DBD7F286A3F8F8L /* 326 */, 0x16CADCE74CFC1063L /* 327 */, + 0x005C19BDFA52C6DDL /* 328 */, 0x68868F5D64D46AD3L /* 329 */, + 0x3A9D512CCF1E186AL /* 330 */, 0x367E62C2385660AEL /* 331 */, + 0xE359E7EA77DCB1D7L /* 332 */, 0x526C0773749ABE6EL /* 333 */, + 0x735AE5F9D09F734BL /* 334 */, 0x493FC7CC8A558BA8L /* 335 */, + 0xB0B9C1533041AB45L /* 336 */, 0x321958BA470A59BDL /* 337 */, + 0x852DB00B5F46C393L /* 338 */, 0x91209B2BD336B0E5L /* 339 */, + 0x6E604F7D659EF19FL /* 340 */, 0xB99A8AE2782CCB24L /* 341 */, + 0xCCF52AB6C814C4C7L /* 342 */, 0x4727D9AFBE11727BL /* 343 */, + 0x7E950D0C0121B34DL /* 344 */, 0x756F435670AD471FL /* 345 */, + 0xF5ADD442615A6849L /* 346 */, 0x4E87E09980B9957AL /* 347 */, + 0x2ACFA1DF50AEE355L /* 348 */, 0xD898263AFD2FD556L /* 349 */, + 0xC8F4924DD80C8FD6L /* 350 */, 0xCF99CA3D754A173AL /* 351 */, + 0xFE477BACAF91BF3CL /* 352 */, 0xED5371F6D690C12DL /* 353 */, + 0x831A5C285E687094L /* 354 */, 0xC5D3C90A3708A0A4L /* 355 */, + 0x0F7F903717D06580L /* 356 */, 0x19F9BB13B8FDF27FL /* 357 */, + 0xB1BD6F1B4D502843L /* 358 */, 0x1C761BA38FFF4012L /* 359 */, + 0x0D1530C4E2E21F3BL /* 360 */, 0x8943CE69A7372C8AL /* 361 */, + 0xE5184E11FEB5CE66L /* 362 */, 0x618BDB80BD736621L /* 363 */, + 0x7D29BAD68B574D0BL /* 364 */, 0x81BB613E25E6FE5BL /* 365 */, + 0x071C9C10BC07913FL /* 366 */, 0xC7BEEB7909AC2D97L /* 367 */, + 0xC3E58D353BC5D757L /* 368 */, 0xEB017892F38F61E8L /* 369 */, + 0xD4EFFB9C9B1CC21AL /* 370 */, 0x99727D26F494F7ABL /* 371 */, + 0xA3E063A2956B3E03L /* 372 */, 0x9D4A8B9A4AA09C30L /* 373 */, + 0x3F6AB7D500090FB4L /* 374 */, 0x9CC0F2A057268AC0L /* 375 */, + 0x3DEE9D2DEDBF42D1L /* 376 */, 0x330F49C87960A972L /* 377 */, + 0xC6B2720287421B41L /* 378 */, 0x0AC59EC07C00369CL /* 379 */, + 0xEF4EAC49CB353425L /* 380 */, 0xF450244EEF0129D8L /* 381 */, + 0x8ACC46E5CAF4DEB6L /* 382 */, 0x2FFEAB63989263F7L /* 383 */, + 0x8F7CB9FE5D7A4578L /* 384 */, 0x5BD8F7644E634635L /* 385 */, + 0x427A7315BF2DC900L /* 386 */, 0x17D0C4AA2125261CL /* 387 */, + 0x3992486C93518E50L /* 388 */, 0xB4CBFEE0A2D7D4C3L /* 389 */, + 0x7C75D6202C5DDD8DL /* 390 */, 0xDBC295D8E35B6C61L /* 391 */, + 0x60B369D302032B19L /* 392 */, 0xCE42685FDCE44132L /* 393 */, + 0x06F3DDB9DDF65610L /* 394 */, 0x8EA4D21DB5E148F0L /* 395 */, + 0x20B0FCE62FCD496FL /* 396 */, 0x2C1B912358B0EE31L /* 397 */, + 0xB28317B818F5A308L /* 398 */, 0xA89C1E189CA6D2CFL /* 399 */, + 0x0C6B18576AAADBC8L /* 400 */, 0xB65DEAA91299FAE3L /* 401 */, + 0xFB2B794B7F1027E7L /* 402 */, 0x04E4317F443B5BEBL /* 403 */, + 0x4B852D325939D0A6L /* 404 */, 0xD5AE6BEEFB207FFCL /* 405 */, + 0x309682B281C7D374L /* 406 */, 0xBAE309A194C3B475L /* 407 */, + 0x8CC3F97B13B49F05L /* 408 */, 0x98A9422FF8293967L /* 409 */, + 0x244B16B01076FF7CL /* 410 */, 0xF8BF571C663D67EEL /* 411 */, + 0x1F0D6758EEE30DA1L /* 412 */, 0xC9B611D97ADEB9B7L /* 413 */, + 0xB7AFD5887B6C57A2L /* 414 */, 0x6290AE846B984FE1L /* 415 */, + 0x94DF4CDEACC1A5FDL /* 416 */, 0x058A5BD1C5483AFFL /* 417 */, + 0x63166CC142BA3C37L /* 418 */, 0x8DB8526EB2F76F40L /* 419 */, + 0xE10880036F0D6D4EL /* 420 */, 0x9E0523C9971D311DL /* 421 */, + 0x45EC2824CC7CD691L /* 422 */, 0x575B8359E62382C9L /* 423 */, + 0xFA9E400DC4889995L /* 424 */, 0xD1823ECB45721568L /* 425 */, + 0xDAFD983B8206082FL /* 426 */, 0xAA7D29082386A8CBL /* 427 */, + 0x269FCD4403B87588L /* 428 */, 0x1B91F5F728BDD1E0L /* 429 */, + 0xE4669F39040201F6L /* 430 */, 0x7A1D7C218CF04ADEL /* 431 */, + 0x65623C29D79CE5CEL /* 432 */, 0x2368449096C00BB1L /* 433 */, + 0xAB9BF1879DA503BAL /* 434 */, 0xBC23ECB1A458058EL /* 435 */, + 0x9A58DF01BB401ECCL /* 436 */, 0xA070E868A85F143DL /* 437 */, + 0x4FF188307DF2239EL /* 438 */, 0x14D565B41A641183L /* 439 */, + 0xEE13337452701602L /* 440 */, 0x950E3DCF3F285E09L /* 441 */, + 0x59930254B9C80953L /* 442 */, 0x3BF299408930DA6DL /* 443 */, + 0xA955943F53691387L /* 444 */, 0xA15EDECAA9CB8784L /* 445 */, + 0x29142127352BE9A0L /* 446 */, 0x76F0371FFF4E7AFBL /* 447 */, + 0x0239F450274F2228L /* 448 */, 0xBB073AF01D5E868BL /* 449 */, + 0xBFC80571C10E96C1L /* 450 */, 0xD267088568222E23L /* 451 */, + 0x9671A3D48E80B5B0L /* 452 */, 0x55B5D38AE193BB81L /* 453 */, + 0x693AE2D0A18B04B8L /* 454 */, 0x5C48B4ECADD5335FL /* 455 */, + 0xFD743B194916A1CAL /* 456 */, 0x2577018134BE98C4L /* 457 */, + 0xE77987E83C54A4ADL /* 458 */, 0x28E11014DA33E1B9L /* 459 */, + 0x270CC59E226AA213L /* 460 */, 0x71495F756D1A5F60L /* 461 */, + 0x9BE853FB60AFEF77L /* 462 */, 0xADC786A7F7443DBFL /* 463 */, + 0x0904456173B29A82L /* 464 */, 0x58BC7A66C232BD5EL /* 465 */, + 0xF306558C673AC8B2L /* 466 */, 0x41F639C6B6C9772AL /* 467 */, + 0x216DEFE99FDA35DAL /* 468 */, 0x11640CC71C7BE615L /* 469 */, + 0x93C43694565C5527L /* 470 */, 0xEA038E6246777839L /* 471 */, + 0xF9ABF3CE5A3E2469L /* 472 */, 0x741E768D0FD312D2L /* 473 */, + 0x0144B883CED652C6L /* 474 */, 0xC20B5A5BA33F8552L /* 475 */, + 0x1AE69633C3435A9DL /* 476 */, 0x97A28CA4088CFDECL /* 477 */, + 0x8824A43C1E96F420L /* 478 */, 0x37612FA66EEEA746L /* 479 */, + 0x6B4CB165F9CF0E5AL /* 480 */, 0x43AA1C06A0ABFB4AL /* 481 */, + 0x7F4DC26FF162796BL /* 482 */, 0x6CBACC8E54ED9B0FL /* 483 */, + 0xA6B7FFEFD2BB253EL /* 484 */, 0x2E25BC95B0A29D4FL /* 485 */, + 0x86D6A58BDEF1388CL /* 486 */, 0xDED74AC576B6F054L /* 487 */, + 0x8030BDBC2B45805DL /* 488 */, 0x3C81AF70E94D9289L /* 489 */, + 0x3EFF6DDA9E3100DBL /* 490 */, 0xB38DC39FDFCC8847L /* 491 */, + 0x123885528D17B87EL /* 492 */, 0xF2DA0ED240B1B642L /* 493 */, + 0x44CEFADCD54BF9A9L /* 494 */, 0x1312200E433C7EE6L /* 495 */, + 0x9FFCC84F3A78C748L /* 496 */, 0xF0CD1F72248576BBL /* 497 */, + 0xEC6974053638CFE4L /* 498 */, 0x2BA7B67C0CEC4E4CL /* 499 */, + 0xAC2F4DF3E5CE32EDL /* 500 */, 0xCB33D14326EA4C11L /* 501 */, + 0xA4E9044CC77E58BCL /* 502 */, 0x5F513293D934FCEFL /* 503 */, + 0x5DC9645506E55444L /* 504 */, 0x50DE418F317DE40AL /* 505 */, + 0x388CB31A69DDE259L /* 506 */, 0x2DB4A83455820A86L /* 507 */, + 0x9010A91E84711AE9L /* 508 */, 0x4DF7F0B7B1498371L /* 509 */, + 0xD62A2EABC0977179L /* 510 */, 0x22FAC097AA8D5C0EL /* 511 */, + }; + + private static final long[] t3 = { + 0xF49FCC2FF1DAF39BL /* 512 */, 0x487FD5C66FF29281L /* 513 */, + 0xE8A30667FCDCA83FL /* 514 */, 0x2C9B4BE3D2FCCE63L /* 515 */, + 0xDA3FF74B93FBBBC2L /* 516 */, 0x2FA165D2FE70BA66L /* 517 */, + 0xA103E279970E93D4L /* 518 */, 0xBECDEC77B0E45E71L /* 519 */, + 0xCFB41E723985E497L /* 520 */, 0xB70AAA025EF75017L /* 521 */, + 0xD42309F03840B8E0L /* 522 */, 0x8EFC1AD035898579L /* 523 */, + 0x96C6920BE2B2ABC5L /* 524 */, 0x66AF4163375A9172L /* 525 */, + 0x2174ABDCCA7127FBL /* 526 */, 0xB33CCEA64A72FF41L /* 527 */, + 0xF04A4933083066A5L /* 528 */, 0x8D970ACDD7289AF5L /* 529 */, + 0x8F96E8E031C8C25EL /* 530 */, 0xF3FEC02276875D47L /* 531 */, + 0xEC7BF310056190DDL /* 532 */, 0xF5ADB0AEBB0F1491L /* 533 */, + 0x9B50F8850FD58892L /* 534 */, 0x4975488358B74DE8L /* 535 */, + 0xA3354FF691531C61L /* 536 */, 0x0702BBE481D2C6EEL /* 537 */, + 0x89FB24057DEDED98L /* 538 */, 0xAC3075138596E902L /* 539 */, + 0x1D2D3580172772EDL /* 540 */, 0xEB738FC28E6BC30DL /* 541 */, + 0x5854EF8F63044326L /* 542 */, 0x9E5C52325ADD3BBEL /* 543 */, + 0x90AA53CF325C4623L /* 544 */, 0xC1D24D51349DD067L /* 545 */, + 0x2051CFEEA69EA624L /* 546 */, 0x13220F0A862E7E4FL /* 547 */, + 0xCE39399404E04864L /* 548 */, 0xD9C42CA47086FCB7L /* 549 */, + 0x685AD2238A03E7CCL /* 550 */, 0x066484B2AB2FF1DBL /* 551 */, + 0xFE9D5D70EFBF79ECL /* 552 */, 0x5B13B9DD9C481854L /* 553 */, + 0x15F0D475ED1509ADL /* 554 */, 0x0BEBCD060EC79851L /* 555 */, + 0xD58C6791183AB7F8L /* 556 */, 0xD1187C5052F3EEE4L /* 557 */, + 0xC95D1192E54E82FFL /* 558 */, 0x86EEA14CB9AC6CA2L /* 559 */, + 0x3485BEB153677D5DL /* 560 */, 0xDD191D781F8C492AL /* 561 */, + 0xF60866BAA784EBF9L /* 562 */, 0x518F643BA2D08C74L /* 563 */, + 0x8852E956E1087C22L /* 564 */, 0xA768CB8DC410AE8DL /* 565 */, + 0x38047726BFEC8E1AL /* 566 */, 0xA67738B4CD3B45AAL /* 567 */, + 0xAD16691CEC0DDE19L /* 568 */, 0xC6D4319380462E07L /* 569 */, + 0xC5A5876D0BA61938L /* 570 */, 0x16B9FA1FA58FD840L /* 571 */, + 0x188AB1173CA74F18L /* 572 */, 0xABDA2F98C99C021FL /* 573 */, + 0x3E0580AB134AE816L /* 574 */, 0x5F3B05B773645ABBL /* 575 */, + 0x2501A2BE5575F2F6L /* 576 */, 0x1B2F74004E7E8BA9L /* 577 */, + 0x1CD7580371E8D953L /* 578 */, 0x7F6ED89562764E30L /* 579 */, + 0xB15926FF596F003DL /* 580 */, 0x9F65293DA8C5D6B9L /* 581 */, + 0x6ECEF04DD690F84CL /* 582 */, 0x4782275FFF33AF88L /* 583 */, + 0xE41433083F820801L /* 584 */, 0xFD0DFE409A1AF9B5L /* 585 */, + 0x4325A3342CDB396BL /* 586 */, 0x8AE77E62B301B252L /* 587 */, + 0xC36F9E9F6655615AL /* 588 */, 0x85455A2D92D32C09L /* 589 */, + 0xF2C7DEA949477485L /* 590 */, 0x63CFB4C133A39EBAL /* 591 */, + 0x83B040CC6EBC5462L /* 592 */, 0x3B9454C8FDB326B0L /* 593 */, + 0x56F56A9E87FFD78CL /* 594 */, 0x2DC2940D99F42BC6L /* 595 */, + 0x98F7DF096B096E2DL /* 596 */, 0x19A6E01E3AD852BFL /* 597 */, + 0x42A99CCBDBD4B40BL /* 598 */, 0xA59998AF45E9C559L /* 599 */, + 0x366295E807D93186L /* 600 */, 0x6B48181BFAA1F773L /* 601 */, + 0x1FEC57E2157A0A1DL /* 602 */, 0x4667446AF6201AD5L /* 603 */, + 0xE615EBCACFB0F075L /* 604 */, 0xB8F31F4F68290778L /* 605 */, + 0x22713ED6CE22D11EL /* 606 */, 0x3057C1A72EC3C93BL /* 607 */, + 0xCB46ACC37C3F1F2FL /* 608 */, 0xDBB893FD02AAF50EL /* 609 */, + 0x331FD92E600B9FCFL /* 610 */, 0xA498F96148EA3AD6L /* 611 */, + 0xA8D8426E8B6A83EAL /* 612 */, 0xA089B274B7735CDCL /* 613 */, + 0x87F6B3731E524A11L /* 614 */, 0x118808E5CBC96749L /* 615 */, + 0x9906E4C7B19BD394L /* 616 */, 0xAFED7F7E9B24A20CL /* 617 */, + 0x6509EADEEB3644A7L /* 618 */, 0x6C1EF1D3E8EF0EDEL /* 619 */, + 0xB9C97D43E9798FB4L /* 620 */, 0xA2F2D784740C28A3L /* 621 */, + 0x7B8496476197566FL /* 622 */, 0x7A5BE3E6B65F069DL /* 623 */, + 0xF96330ED78BE6F10L /* 624 */, 0xEEE60DE77A076A15L /* 625 */, + 0x2B4BEE4AA08B9BD0L /* 626 */, 0x6A56A63EC7B8894EL /* 627 */, + 0x02121359BA34FEF4L /* 628 */, 0x4CBF99F8283703FCL /* 629 */, + 0x398071350CAF30C8L /* 630 */, 0xD0A77A89F017687AL /* 631 */, + 0xF1C1A9EB9E423569L /* 632 */, 0x8C7976282DEE8199L /* 633 */, + 0x5D1737A5DD1F7ABDL /* 634 */, 0x4F53433C09A9FA80L /* 635 */, + 0xFA8B0C53DF7CA1D9L /* 636 */, 0x3FD9DCBC886CCB77L /* 637 */, + 0xC040917CA91B4720L /* 638 */, 0x7DD00142F9D1DCDFL /* 639 */, + 0x8476FC1D4F387B58L /* 640 */, 0x23F8E7C5F3316503L /* 641 */, + 0x032A2244E7E37339L /* 642 */, 0x5C87A5D750F5A74BL /* 643 */, + 0x082B4CC43698992EL /* 644 */, 0xDF917BECB858F63CL /* 645 */, + 0x3270B8FC5BF86DDAL /* 646 */, 0x10AE72BB29B5DD76L /* 647 */, + 0x576AC94E7700362BL /* 648 */, 0x1AD112DAC61EFB8FL /* 649 */, + 0x691BC30EC5FAA427L /* 650 */, 0xFF246311CC327143L /* 651 */, + 0x3142368E30E53206L /* 652 */, 0x71380E31E02CA396L /* 653 */, + 0x958D5C960AAD76F1L /* 654 */, 0xF8D6F430C16DA536L /* 655 */, + 0xC8FFD13F1BE7E1D2L /* 656 */, 0x7578AE66004DDBE1L /* 657 */, + 0x05833F01067BE646L /* 658 */, 0xBB34B5AD3BFE586DL /* 659 */, + 0x095F34C9A12B97F0L /* 660 */, 0x247AB64525D60CA8L /* 661 */, + 0xDCDBC6F3017477D1L /* 662 */, 0x4A2E14D4DECAD24DL /* 663 */, + 0xBDB5E6D9BE0A1EEBL /* 664 */, 0x2A7E70F7794301ABL /* 665 */, + 0xDEF42D8A270540FDL /* 666 */, 0x01078EC0A34C22C1L /* 667 */, + 0xE5DE511AF4C16387L /* 668 */, 0x7EBB3A52BD9A330AL /* 669 */, + 0x77697857AA7D6435L /* 670 */, 0x004E831603AE4C32L /* 671 */, + 0xE7A21020AD78E312L /* 672 */, 0x9D41A70C6AB420F2L /* 673 */, + 0x28E06C18EA1141E6L /* 674 */, 0xD2B28CBD984F6B28L /* 675 */, + 0x26B75F6C446E9D83L /* 676 */, 0xBA47568C4D418D7FL /* 677 */, + 0xD80BADBFE6183D8EL /* 678 */, 0x0E206D7F5F166044L /* 679 */, + 0xE258A43911CBCA3EL /* 680 */, 0x723A1746B21DC0BCL /* 681 */, + 0xC7CAA854F5D7CDD3L /* 682 */, 0x7CAC32883D261D9CL /* 683 */, + 0x7690C26423BA942CL /* 684 */, 0x17E55524478042B8L /* 685 */, + 0xE0BE477656A2389FL /* 686 */, 0x4D289B5E67AB2DA0L /* 687 */, + 0x44862B9C8FBBFD31L /* 688 */, 0xB47CC8049D141365L /* 689 */, + 0x822C1B362B91C793L /* 690 */, 0x4EB14655FB13DFD8L /* 691 */, + 0x1ECBBA0714E2A97BL /* 692 */, 0x6143459D5CDE5F14L /* 693 */, + 0x53A8FBF1D5F0AC89L /* 694 */, 0x97EA04D81C5E5B00L /* 695 */, + 0x622181A8D4FDB3F3L /* 696 */, 0xE9BCD341572A1208L /* 697 */, + 0x1411258643CCE58AL /* 698 */, 0x9144C5FEA4C6E0A4L /* 699 */, + 0x0D33D06565CF620FL /* 700 */, 0x54A48D489F219CA1L /* 701 */, + 0xC43E5EAC6D63C821L /* 702 */, 0xA9728B3A72770DAFL /* 703 */, + 0xD7934E7B20DF87EFL /* 704 */, 0xE35503B61A3E86E5L /* 705 */, + 0xCAE321FBC819D504L /* 706 */, 0x129A50B3AC60BFA6L /* 707 */, + 0xCD5E68EA7E9FB6C3L /* 708 */, 0xB01C90199483B1C7L /* 709 */, + 0x3DE93CD5C295376CL /* 710 */, 0xAED52EDF2AB9AD13L /* 711 */, + 0x2E60F512C0A07884L /* 712 */, 0xBC3D86A3E36210C9L /* 713 */, + 0x35269D9B163951CEL /* 714 */, 0x0C7D6E2AD0CDB5FAL /* 715 */, + 0x59E86297D87F5733L /* 716 */, 0x298EF221898DB0E7L /* 717 */, + 0x55000029D1A5AA7EL /* 718 */, 0x8BC08AE1B5061B45L /* 719 */, + 0xC2C31C2B6C92703AL /* 720 */, 0x94CC596BAF25EF42L /* 721 */, + 0x0A1D73DB22540456L /* 722 */, 0x04B6A0F9D9C4179AL /* 723 */, + 0xEFFDAFA2AE3D3C60L /* 724 */, 0xF7C8075BB49496C4L /* 725 */, + 0x9CC5C7141D1CD4E3L /* 726 */, 0x78BD1638218E5534L /* 727 */, + 0xB2F11568F850246AL /* 728 */, 0xEDFABCFA9502BC29L /* 729 */, + 0x796CE5F2DA23051BL /* 730 */, 0xAAE128B0DC93537CL /* 731 */, + 0x3A493DA0EE4B29AEL /* 732 */, 0xB5DF6B2C416895D7L /* 733 */, + 0xFCABBD25122D7F37L /* 734 */, 0x70810B58105DC4B1L /* 735 */, + 0xE10FDD37F7882A90L /* 736 */, 0x524DCAB5518A3F5CL /* 737 */, + 0x3C9E85878451255BL /* 738 */, 0x4029828119BD34E2L /* 739 */, + 0x74A05B6F5D3CECCBL /* 740 */, 0xB610021542E13ECAL /* 741 */, + 0x0FF979D12F59E2ACL /* 742 */, 0x6037DA27E4F9CC50L /* 743 */, + 0x5E92975A0DF1847DL /* 744 */, 0xD66DE190D3E623FEL /* 745 */, + 0x5032D6B87B568048L /* 746 */, 0x9A36B7CE8235216EL /* 747 */, + 0x80272A7A24F64B4AL /* 748 */, 0x93EFED8B8C6916F7L /* 749 */, + 0x37DDBFF44CCE1555L /* 750 */, 0x4B95DB5D4B99BD25L /* 751 */, + 0x92D3FDA169812FC0L /* 752 */, 0xFB1A4A9A90660BB6L /* 753 */, + 0x730C196946A4B9B2L /* 754 */, 0x81E289AA7F49DA68L /* 755 */, + 0x64669A0F83B1A05FL /* 756 */, 0x27B3FF7D9644F48BL /* 757 */, + 0xCC6B615C8DB675B3L /* 758 */, 0x674F20B9BCEBBE95L /* 759 */, + 0x6F31238275655982L /* 760 */, 0x5AE488713E45CF05L /* 761 */, + 0xBF619F9954C21157L /* 762 */, 0xEABAC46040A8EAE9L /* 763 */, + 0x454C6FE9F2C0C1CDL /* 764 */, 0x419CF6496412691CL /* 765 */, + 0xD3DC3BEF265B0F70L /* 766 */, 0x6D0E60F5C3578A9EL /* 767 */, + }; + + private static final long[] t4 = { + 0x5B0E608526323C55L /* 768 */, 0x1A46C1A9FA1B59F5L /* 769 */, + 0xA9E245A17C4C8FFAL /* 770 */, 0x65CA5159DB2955D7L /* 771 */, + 0x05DB0A76CE35AFC2L /* 772 */, 0x81EAC77EA9113D45L /* 773 */, + 0x528EF88AB6AC0A0DL /* 774 */, 0xA09EA253597BE3FFL /* 775 */, + 0x430DDFB3AC48CD56L /* 776 */, 0xC4B3A67AF45CE46FL /* 777 */, + 0x4ECECFD8FBE2D05EL /* 778 */, 0x3EF56F10B39935F0L /* 779 */, + 0x0B22D6829CD619C6L /* 780 */, 0x17FD460A74DF2069L /* 781 */, + 0x6CF8CC8E8510ED40L /* 782 */, 0xD6C824BF3A6ECAA7L /* 783 */, + 0x61243D581A817049L /* 784 */, 0x048BACB6BBC163A2L /* 785 */, + 0xD9A38AC27D44CC32L /* 786 */, 0x7FDDFF5BAAF410ABL /* 787 */, + 0xAD6D495AA804824BL /* 788 */, 0xE1A6A74F2D8C9F94L /* 789 */, + 0xD4F7851235DEE8E3L /* 790 */, 0xFD4B7F886540D893L /* 791 */, + 0x247C20042AA4BFDAL /* 792 */, 0x096EA1C517D1327CL /* 793 */, + 0xD56966B4361A6685L /* 794 */, 0x277DA5C31221057DL /* 795 */, + 0x94D59893A43ACFF7L /* 796 */, 0x64F0C51CCDC02281L /* 797 */, + 0x3D33BCC4FF6189DBL /* 798 */, 0xE005CB184CE66AF1L /* 799 */, + 0xFF5CCD1D1DB99BEAL /* 800 */, 0xB0B854A7FE42980FL /* 801 */, + 0x7BD46A6A718D4B9FL /* 802 */, 0xD10FA8CC22A5FD8CL /* 803 */, + 0xD31484952BE4BD31L /* 804 */, 0xC7FA975FCB243847L /* 805 */, + 0x4886ED1E5846C407L /* 806 */, 0x28CDDB791EB70B04L /* 807 */, + 0xC2B00BE2F573417FL /* 808 */, 0x5C9590452180F877L /* 809 */, + 0x7A6BDDFFF370EB00L /* 810 */, 0xCE509E38D6D9D6A4L /* 811 */, + 0xEBEB0F00647FA702L /* 812 */, 0x1DCC06CF76606F06L /* 813 */, + 0xE4D9F28BA286FF0AL /* 814 */, 0xD85A305DC918C262L /* 815 */, + 0x475B1D8732225F54L /* 816 */, 0x2D4FB51668CCB5FEL /* 817 */, + 0xA679B9D9D72BBA20L /* 818 */, 0x53841C0D912D43A5L /* 819 */, + 0x3B7EAA48BF12A4E8L /* 820 */, 0x781E0E47F22F1DDFL /* 821 */, + 0xEFF20CE60AB50973L /* 822 */, 0x20D261D19DFFB742L /* 823 */, + 0x16A12B03062A2E39L /* 824 */, 0x1960EB2239650495L /* 825 */, + 0x251C16FED50EB8B8L /* 826 */, 0x9AC0C330F826016EL /* 827 */, + 0xED152665953E7671L /* 828 */, 0x02D63194A6369570L /* 829 */, + 0x5074F08394B1C987L /* 830 */, 0x70BA598C90B25CE1L /* 831 */, + 0x794A15810B9742F6L /* 832 */, 0x0D5925E9FCAF8C6CL /* 833 */, + 0x3067716CD868744EL /* 834 */, 0x910AB077E8D7731BL /* 835 */, + 0x6A61BBDB5AC42F61L /* 836 */, 0x93513EFBF0851567L /* 837 */, + 0xF494724B9E83E9D5L /* 838 */, 0xE887E1985C09648DL /* 839 */, + 0x34B1D3C675370CFDL /* 840 */, 0xDC35E433BC0D255DL /* 841 */, + 0xD0AAB84234131BE0L /* 842 */, 0x08042A50B48B7EAFL /* 843 */, + 0x9997C4EE44A3AB35L /* 844 */, 0x829A7B49201799D0L /* 845 */, + 0x263B8307B7C54441L /* 846 */, 0x752F95F4FD6A6CA6L /* 847 */, + 0x927217402C08C6E5L /* 848 */, 0x2A8AB754A795D9EEL /* 849 */, + 0xA442F7552F72943DL /* 850 */, 0x2C31334E19781208L /* 851 */, + 0x4FA98D7CEAEE6291L /* 852 */, 0x55C3862F665DB309L /* 853 */, + 0xBD0610175D53B1F3L /* 854 */, 0x46FE6CB840413F27L /* 855 */, + 0x3FE03792DF0CFA59L /* 856 */, 0xCFE700372EB85E8FL /* 857 */, + 0xA7BE29E7ADBCE118L /* 858 */, 0xE544EE5CDE8431DDL /* 859 */, + 0x8A781B1B41F1873EL /* 860 */, 0xA5C94C78A0D2F0E7L /* 861 */, + 0x39412E2877B60728L /* 862 */, 0xA1265EF3AFC9A62CL /* 863 */, + 0xBCC2770C6A2506C5L /* 864 */, 0x3AB66DD5DCE1CE12L /* 865 */, + 0xE65499D04A675B37L /* 866 */, 0x7D8F523481BFD216L /* 867 */, + 0x0F6F64FCEC15F389L /* 868 */, 0x74EFBE618B5B13C8L /* 869 */, + 0xACDC82B714273E1DL /* 870 */, 0xDD40BFE003199D17L /* 871 */, + 0x37E99257E7E061F8L /* 872 */, 0xFA52626904775AAAL /* 873 */, + 0x8BBBF63A463D56F9L /* 874 */, 0xF0013F1543A26E64L /* 875 */, + 0xA8307E9F879EC898L /* 876 */, 0xCC4C27A4150177CCL /* 877 */, + 0x1B432F2CCA1D3348L /* 878 */, 0xDE1D1F8F9F6FA013L /* 879 */, + 0x606602A047A7DDD6L /* 880 */, 0xD237AB64CC1CB2C7L /* 881 */, + 0x9B938E7225FCD1D3L /* 882 */, 0xEC4E03708E0FF476L /* 883 */, + 0xFEB2FBDA3D03C12DL /* 884 */, 0xAE0BCED2EE43889AL /* 885 */, + 0x22CB8923EBFB4F43L /* 886 */, 0x69360D013CF7396DL /* 887 */, + 0x855E3602D2D4E022L /* 888 */, 0x073805BAD01F784CL /* 889 */, + 0x33E17A133852F546L /* 890 */, 0xDF4874058AC7B638L /* 891 */, + 0xBA92B29C678AA14AL /* 892 */, 0x0CE89FC76CFAADCDL /* 893 */, + 0x5F9D4E0908339E34L /* 894 */, 0xF1AFE9291F5923B9L /* 895 */, + 0x6E3480F60F4A265FL /* 896 */, 0xEEBF3A2AB29B841CL /* 897 */, + 0xE21938A88F91B4ADL /* 898 */, 0x57DFEFF845C6D3C3L /* 899 */, + 0x2F006B0BF62CAAF2L /* 900 */, 0x62F479EF6F75EE78L /* 901 */, + 0x11A55AD41C8916A9L /* 902 */, 0xF229D29084FED453L /* 903 */, + 0x42F1C27B16B000E6L /* 904 */, 0x2B1F76749823C074L /* 905 */, + 0x4B76ECA3C2745360L /* 906 */, 0x8C98F463B91691BDL /* 907 */, + 0x14BCC93CF1ADE66AL /* 908 */, 0x8885213E6D458397L /* 909 */, + 0x8E177DF0274D4711L /* 910 */, 0xB49B73B5503F2951L /* 911 */, + 0x10168168C3F96B6BL /* 912 */, 0x0E3D963B63CAB0AEL /* 913 */, + 0x8DFC4B5655A1DB14L /* 914 */, 0xF789F1356E14DE5CL /* 915 */, + 0x683E68AF4E51DAC1L /* 916 */, 0xC9A84F9D8D4B0FD9L /* 917 */, + 0x3691E03F52A0F9D1L /* 918 */, 0x5ED86E46E1878E80L /* 919 */, + 0x3C711A0E99D07150L /* 920 */, 0x5A0865B20C4E9310L /* 921 */, + 0x56FBFC1FE4F0682EL /* 922 */, 0xEA8D5DE3105EDF9BL /* 923 */, + 0x71ABFDB12379187AL /* 924 */, 0x2EB99DE1BEE77B9CL /* 925 */, + 0x21ECC0EA33CF4523L /* 926 */, 0x59A4D7521805C7A1L /* 927 */, + 0x3896F5EB56AE7C72L /* 928 */, 0xAA638F3DB18F75DCL /* 929 */, + 0x9F39358DABE9808EL /* 930 */, 0xB7DEFA91C00B72ACL /* 931 */, + 0x6B5541FD62492D92L /* 932 */, 0x6DC6DEE8F92E4D5BL /* 933 */, + 0x353F57ABC4BEEA7EL /* 934 */, 0x735769D6DA5690CEL /* 935 */, + 0x0A234AA642391484L /* 936 */, 0xF6F9508028F80D9DL /* 937 */, + 0xB8E319A27AB3F215L /* 938 */, 0x31AD9C1151341A4DL /* 939 */, + 0x773C22A57BEF5805L /* 940 */, 0x45C7561A07968633L /* 941 */, + 0xF913DA9E249DBE36L /* 942 */, 0xDA652D9B78A64C68L /* 943 */, + 0x4C27A97F3BC334EFL /* 944 */, 0x76621220E66B17F4L /* 945 */, + 0x967743899ACD7D0BL /* 946 */, 0xF3EE5BCAE0ED6782L /* 947 */, + 0x409F753600C879FCL /* 948 */, 0x06D09A39B5926DB6L /* 949 */, + 0x6F83AEB0317AC588L /* 950 */, 0x01E6CA4A86381F21L /* 951 */, + 0x66FF3462D19F3025L /* 952 */, 0x72207C24DDFD3BFBL /* 953 */, + 0x4AF6B6D3E2ECE2EBL /* 954 */, 0x9C994DBEC7EA08DEL /* 955 */, + 0x49ACE597B09A8BC4L /* 956 */, 0xB38C4766CF0797BAL /* 957 */, + 0x131B9373C57C2A75L /* 958 */, 0xB1822CCE61931E58L /* 959 */, + 0x9D7555B909BA1C0CL /* 960 */, 0x127FAFDD937D11D2L /* 961 */, + 0x29DA3BADC66D92E4L /* 962 */, 0xA2C1D57154C2ECBCL /* 963 */, + 0x58C5134D82F6FE24L /* 964 */, 0x1C3AE3515B62274FL /* 965 */, + 0xE907C82E01CB8126L /* 966 */, 0xF8ED091913E37FCBL /* 967 */, + 0x3249D8F9C80046C9L /* 968 */, 0x80CF9BEDE388FB63L /* 969 */, + 0x1881539A116CF19EL /* 970 */, 0x5103F3F76BD52457L /* 971 */, + 0x15B7E6F5AE47F7A8L /* 972 */, 0xDBD7C6DED47E9CCFL /* 973 */, + 0x44E55C410228BB1AL /* 974 */, 0xB647D4255EDB4E99L /* 975 */, + 0x5D11882BB8AAFC30L /* 976 */, 0xF5098BBB29D3212AL /* 977 */, + 0x8FB5EA14E90296B3L /* 978 */, 0x677B942157DD025AL /* 979 */, + 0xFB58E7C0A390ACB5L /* 980 */, 0x89D3674C83BD4A01L /* 981 */, + 0x9E2DA4DF4BF3B93BL /* 982 */, 0xFCC41E328CAB4829L /* 983 */, + 0x03F38C96BA582C52L /* 984 */, 0xCAD1BDBD7FD85DB2L /* 985 */, + 0xBBB442C16082AE83L /* 986 */, 0xB95FE86BA5DA9AB0L /* 987 */, + 0xB22E04673771A93FL /* 988 */, 0x845358C9493152D8L /* 989 */, + 0xBE2A488697B4541EL /* 990 */, 0x95A2DC2DD38E6966L /* 991 */, + 0xC02C11AC923C852BL /* 992 */, 0x2388B1990DF2A87BL /* 993 */, + 0x7C8008FA1B4F37BEL /* 994 */, 0x1F70D0C84D54E503L /* 995 */, + 0x5490ADEC7ECE57D4L /* 996 */, 0x002B3C27D9063A3AL /* 997 */, + 0x7EAEA3848030A2BFL /* 998 */, 0xC602326DED2003C0L /* 999 */, + 0x83A7287D69A94086L /* 1000 */, 0xC57A5FCB30F57A8AL /* 1001 */, + 0xB56844E479EBE779L /* 1002 */, 0xA373B40F05DCBCE9L /* 1003 */, + 0xD71A786E88570EE2L /* 1004 */, 0x879CBACDBDE8F6A0L /* 1005 */, + 0x976AD1BCC164A32FL /* 1006 */, 0xAB21E25E9666D78BL /* 1007 */, + 0x901063AAE5E5C33CL /* 1008 */, 0x9818B34448698D90L /* 1009 */, + 0xE36487AE3E1E8ABBL /* 1010 */, 0xAFBDF931893BDCB4L /* 1011 */, + 0x6345A0DC5FBBD519L /* 1012 */, 0x8628FE269B9465CAL /* 1013 */, + 0x1E5D01603F9C51ECL /* 1014 */, 0x4DE44006A15049B7L /* 1015 */, + 0xBF6C70E5F776CBB1L /* 1016 */, 0x411218F2EF552BEDL /* 1017 */, + 0xCB0C0708705A36A3L /* 1018 */, 0xE74D14754F986044L /* 1019 */, + 0xCD56D9430EA8280EL /* 1020 */, 0xC12591D7535F5065L /* 1021 */, + 0xC83223F1720AEF96L /* 1022 */, 0xC3A0396F7363A51FL /* 1023 */ + }; + + private static final int DIGEST_LENGTH = 24; + + // + // registers + // + private long a, b, c; + private long byteCount; + + // + // buffers + // + private byte[] buf = new byte[8]; + private int bOff = 0; + + private long[] x = new long[8]; + private int xOff = 0; + + /** + * Standard constructor + */ + public TigerDigest() + { + reset(); + } + + /** + * Copy constructor. This will copy the state of the provided + * message digest. + */ + public TigerDigest(TigerDigest t) + { + a = t.a; + b = t.b; + c = t.c; + + System.arraycopy(t.x, 0, x, 0, t.x.length); + xOff = t.xOff; + + System.arraycopy(t.buf, 0, buf, 0, t.buf.length); + bOff = t.bOff; + + byteCount = t.byteCount; + } + + public String getAlgorithmName() + { + return "Tiger"; + } + + public int getDigestSize() + { + return DIGEST_LENGTH; + } + + private void processWord( + byte[] b, + int off) + { + x[xOff++] = ((long)(b[off + 7] & 0xff) << 56) + | ((long)(b[off + 6] & 0xff) << 48) + | ((long)(b[off + 5] & 0xff) << 40) + | ((long)(b[off + 4] & 0xff) << 32) + | ((long)(b[off + 3] & 0xff) << 24) + | ((long)(b[off + 2] & 0xff) << 16) + | ((long)(b[off + 1] & 0xff) << 8) + | ((b[off + 0] & 0xff)); + + if (xOff == x.length) + { + processBlock(); + } + + bOff = 0; + } + + public void update( + byte in) + { + buf[bOff++] = in; + + if (bOff == buf.length) + { + processWord(buf, 0); + } + + byteCount++; + } + + public void update( + byte[] in, + int inOff, + int len) + { + // + // fill the current word + // + while ((bOff != 0) && (len > 0)) + { + update(in[inOff]); + + inOff++; + len--; + } + + // + // process whole words. + // + while (len > 8) + { + processWord(in, inOff); + + inOff += 8; + len -= 8; + byteCount += 8; + } + + // + // load in the remainder. + // + while (len > 0) + { + update(in[inOff]); + + inOff++; + len--; + } + } + + private void roundABC( + long x, + long mul) + { + c ^= x ; + a -= t1[(int)c & 0xff] ^ t2[(int)(c >> 16) & 0xff] + ^ t3[(int)(c >> 32) & 0xff] ^ t4[(int)(c >> 48) & 0xff]; + b += t4[(int)(c >> 8) & 0xff] ^ t3[(int)(c >> 24) & 0xff] + ^ t2[(int)(c >> 40) & 0xff] ^ t1[(int)(c >> 56) & 0xff]; + b *= mul; + } + + private void roundBCA( + long x, + long mul) + { + a ^= x ; + b -= t1[(int)a & 0xff] ^ t2[(int)(a >> 16) & 0xff] + ^ t3[(int)(a >> 32) & 0xff] ^ t4[(int)(a >> 48) & 0xff]; + c += t4[(int)(a >> 8) & 0xff] ^ t3[(int)(a >> 24) & 0xff] + ^ t2[(int)(a >> 40) & 0xff] ^ t1[(int)(a >> 56) & 0xff]; + c *= mul; + } + + private void roundCAB( + long x, + long mul) + { + b ^= x ; + c -= t1[(int)b & 0xff] ^ t2[(int)(b >> 16) & 0xff] + ^ t3[(int)(b >> 32) & 0xff] ^ t4[(int)(b >> 48) & 0xff]; + a += t4[(int)(b >> 8) & 0xff] ^ t3[(int)(b >> 24) & 0xff] + ^ t2[(int)(b >> 40) & 0xff] ^ t1[(int)(b >> 56) & 0xff]; + a *= mul; + } + + private void keySchedule() + { + x[0] -= x[7] ^ 0xA5A5A5A5A5A5A5A5L; + x[1] ^= x[0]; + x[2] += x[1]; + x[3] -= x[2] ^ ((~x[1]) << 19); + x[4] ^= x[3]; + x[5] += x[4]; + x[6] -= x[5] ^ ((~x[4]) >>> 23); + x[7] ^= x[6]; + x[0] += x[7]; + x[1] -= x[0] ^ ((~x[7]) << 19); + x[2] ^= x[1]; + x[3] += x[2]; + x[4] -= x[3] ^ ((~x[2]) >>> 23); + x[5] ^= x[4]; + x[6] += x[5]; + x[7] -= x[6] ^ 0x0123456789ABCDEFL; + } + + private void processBlock() + { + // + // save abc + // + long aa = a; + long bb = b; + long cc = c; + + // + // rounds and schedule + // + roundABC(x[0], 5); + roundBCA(x[1], 5); + roundCAB(x[2], 5); + roundABC(x[3], 5); + roundBCA(x[4], 5); + roundCAB(x[5], 5); + roundABC(x[6], 5); + roundBCA(x[7], 5); + + keySchedule(); + + roundCAB(x[0], 7); + roundABC(x[1], 7); + roundBCA(x[2], 7); + roundCAB(x[3], 7); + roundABC(x[4], 7); + roundBCA(x[5], 7); + roundCAB(x[6], 7); + roundABC(x[7], 7); + + keySchedule(); + + roundBCA(x[0], 9); + roundCAB(x[1], 9); + roundABC(x[2], 9); + roundBCA(x[3], 9); + roundCAB(x[4], 9); + roundABC(x[5], 9); + roundBCA(x[6], 9); + roundCAB(x[7], 9); + + // + // feed forward + // + a ^= aa; + b -= bb; + c += cc; + + // + // clear the x buffer + // + xOff = 0; + for (int i = 0; i != x.length; i++) + { + x[i] = 0; + } + } + + public void unpackWord( + long r, + byte[] out, + int outOff) + { + out[outOff + 7] = (byte)(r >> 56); + out[outOff + 6] = (byte)(r >> 48); + out[outOff + 5] = (byte)(r >> 40); + out[outOff + 4] = (byte)(r >> 32); + out[outOff + 3] = (byte)(r >> 24); + out[outOff + 2] = (byte)(r >> 16); + out[outOff + 1] = (byte)(r >> 8); + out[outOff] = (byte)r; + } + + private void processLength( + long bitLength) + { + x[7] = bitLength; + } + + private void finish() + { + long bitLength = (byteCount << 3); + + update((byte)0x01); + + while (bOff != 0) + { + update((byte)0); + } + + processLength(bitLength); + + processBlock(); + } + + public int doFinal( + byte[] out, + int outOff) + { + finish(); + + unpackWord(a, out, outOff); + unpackWord(b, out, outOff + 8); + unpackWord(c, out, outOff + 16); + + reset(); + + return DIGEST_LENGTH; + } + + /** + * reset the chaining variables + */ + public void reset() + { + a = 0x0123456789ABCDEFL; + b = 0xFEDCBA9876543210L; + c = 0xF096A5B4C3B2E187L; + + xOff = 0; + for (int i = 0; i != x.length; i++) + { + x[i] = 0; + } + + bOff = 0; + for (int i = 0; i != buf.length; i++) + { + buf[i] = 0; + } + + byteCount = 0; + } + + public int getByteLength() + { + return BYTE_LENGTH; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/engines/Salsa20Engine.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/engines/Salsa20Engine.java new file mode 100644 index 00000000..c51d120b --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/engines/Salsa20Engine.java @@ -0,0 +1,377 @@ +package org.bouncycastle.crypto.engines; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.MaxBytesExceededException; +import org.bouncycastle.crypto.StreamCipher; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; + +/** + * Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005 + */ + +public class Salsa20Engine + implements StreamCipher +{ + /** Constants */ + private final static int stateSize = 16; // 16, 32 bit ints = 64 bytes + + private final static byte[] + sigma = toByteArray("expand 32-byte k"), + tau = toByteArray("expand 16-byte k"); + + // No sure why the version in org.bouncycastle.util.Strings was throwing NoSuchMethodErrors + private static byte[] toByteArray(String string) + { + byte[] bytes = new byte[string.length()]; + + for (int i = 0; i != bytes.length; i++) + { + char ch = string.charAt(i); + + bytes[i] = (byte)ch; + } + + return bytes; + } + + + /* + * variables to hold the state of the engine + * during encryption and decryption + */ + private int index = 0; + private int[] engineState = new int[stateSize]; // state + private int[] x = new int[stateSize] ; // internal buffer + private byte[] keyStream = new byte[stateSize * 4], // expanded state, 64 bytes + workingKey = null, + workingIV = null; + private boolean initialised = false; + + /* + * internal counter + */ + private int cW0, cW1, cW2; + + + /** + * initialise a Salsa20 cipher. + * + * @param forEncryption whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean forEncryption, + CipherParameters params) + { + /* + * Salsa20 encryption and decryption is completely + * symmetrical, so the 'forEncryption' is + * irrelevant. (Like 90% of stream ciphers) + */ + + if (!(params instanceof ParametersWithIV)) + { + throw new IllegalArgumentException("Salsa20 Init parameters must include an IV"); + } + + ParametersWithIV ivParams = (ParametersWithIV) params; + + byte[] iv = ivParams.getIV(); + + if (iv == null || iv.length != 8) + { + throw new IllegalArgumentException("Salsa20 requires exactly 8 bytes of IV"); + } + + if (!(ivParams.getParameters() instanceof KeyParameter)) + { + throw new IllegalArgumentException("Salsa20 Init parameters must include a key"); + } + + KeyParameter key = (KeyParameter) ivParams.getParameters(); + + workingKey = key.getKey(); + workingIV = iv; + + setKey(workingKey, workingIV); + } + + public String getAlgorithmName() + { + return "Salsa20"; + } + + public byte returnByte(byte in) + { + if (limitExceeded()) + { + throw new MaxBytesExceededException("2^70 byte limit per IV; Change IV"); + } + + if (index == 0) + { + salsa20WordToByte(engineState, keyStream); + engineState[8]++; + if (engineState[8] == 0) + { + engineState[9]++; + } + } + byte out = (byte)(keyStream[index]^in); + index = (index + 1) & 63; + + return out; + } + + public void processBytes( + byte[] in, + int inOff, + int len, + byte[] out, + int outOff) + { + if (!initialised) + { + throw new IllegalStateException(getAlgorithmName()+" not initialised"); + } + + if ((inOff + len) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + len) > out.length) + { + throw new DataLengthException("output buffer too short"); + } + + if (limitExceeded(len)) + { + throw new MaxBytesExceededException("2^70 byte limit per IV would be exceeded; Change IV"); + } + + for (int i = 0; i < len; i++) + { + if (index == 0) + { + salsa20WordToByte(engineState, keyStream); + engineState[8]++; + if (engineState[8] == 0) + { + engineState[9]++; + } + } + out[i+outOff] = (byte)(keyStream[index]^in[i+inOff]); + index = (index + 1) & 63; + } + } + + public void reset() + { + setKey(workingKey, workingIV); + } + + // Private implementation + + private void setKey(byte[] keyBytes, byte[] ivBytes) + { + workingKey = keyBytes; + workingIV = ivBytes; + + index = 0; + resetCounter(); + int offset = 0; + byte[] constants; + + // Key + engineState[1] = byteToIntLittle(workingKey, 0); + engineState[2] = byteToIntLittle(workingKey, 4); + engineState[3] = byteToIntLittle(workingKey, 8); + engineState[4] = byteToIntLittle(workingKey, 12); + + if (workingKey.length == 32) + { + constants = sigma; + offset = 16; + } + else + { + constants = tau; + } + + engineState[11] = byteToIntLittle(workingKey, offset); + engineState[12] = byteToIntLittle(workingKey, offset+4); + engineState[13] = byteToIntLittle(workingKey, offset+8); + engineState[14] = byteToIntLittle(workingKey, offset+12); + engineState[0 ] = byteToIntLittle(constants, 0); + engineState[5 ] = byteToIntLittle(constants, 4); + engineState[10] = byteToIntLittle(constants, 8); + engineState[15] = byteToIntLittle(constants, 12); + + // IV + engineState[6] = byteToIntLittle(workingIV, 0); + engineState[7] = byteToIntLittle(workingIV, 4); + engineState[8] = engineState[9] = 0; + + initialised = true; + } + + /** + * Salsa20 function + * + * @param input input data + * + * @return keystream + */ + private void salsa20WordToByte(int[] input, byte[] output) + { + System.arraycopy(input, 0, x, 0, input.length); + + for (int i = 0; i < 10; i++) + { + x[ 4] ^= rotl((x[ 0]+x[12]), 7); + x[ 8] ^= rotl((x[ 4]+x[ 0]), 9); + x[12] ^= rotl((x[ 8]+x[ 4]),13); + x[ 0] ^= rotl((x[12]+x[ 8]),18); + x[ 9] ^= rotl((x[ 5]+x[ 1]), 7); + x[13] ^= rotl((x[ 9]+x[ 5]), 9); + x[ 1] ^= rotl((x[13]+x[ 9]),13); + x[ 5] ^= rotl((x[ 1]+x[13]),18); + x[14] ^= rotl((x[10]+x[ 6]), 7); + x[ 2] ^= rotl((x[14]+x[10]), 9); + x[ 6] ^= rotl((x[ 2]+x[14]),13); + x[10] ^= rotl((x[ 6]+x[ 2]),18); + x[ 3] ^= rotl((x[15]+x[11]), 7); + x[ 7] ^= rotl((x[ 3]+x[15]), 9); + x[11] ^= rotl((x[ 7]+x[ 3]),13); + x[15] ^= rotl((x[11]+x[ 7]),18); + x[ 1] ^= rotl((x[ 0]+x[ 3]), 7); + x[ 2] ^= rotl((x[ 1]+x[ 0]), 9); + x[ 3] ^= rotl((x[ 2]+x[ 1]),13); + x[ 0] ^= rotl((x[ 3]+x[ 2]),18); + x[ 6] ^= rotl((x[ 5]+x[ 4]), 7); + x[ 7] ^= rotl((x[ 6]+x[ 5]), 9); + x[ 4] ^= rotl((x[ 7]+x[ 6]),13); + x[ 5] ^= rotl((x[ 4]+x[ 7]),18); + x[11] ^= rotl((x[10]+x[ 9]), 7); + x[ 8] ^= rotl((x[11]+x[10]), 9); + x[ 9] ^= rotl((x[ 8]+x[11]),13); + x[10] ^= rotl((x[ 9]+x[ 8]),18); + x[12] ^= rotl((x[15]+x[14]), 7); + x[13] ^= rotl((x[12]+x[15]), 9); + x[14] ^= rotl((x[13]+x[12]),13); + x[15] ^= rotl((x[14]+x[13]),18); + } + + int offset = 0; + for (int i = 0; i < stateSize; i++) + { + intToByteLittle(x[i] + input[i], output, offset); + offset += 4; + } + + for (int i = stateSize; i < x.length; i++) + { + intToByteLittle(x[i], output, offset); + offset += 4; + } + } + + /** + * 32 bit word to 4 byte array in little endian order + * + * @param x value to 'unpack' + * + * @return value of x expressed as a byte[] array in little endian order + */ + private byte[] intToByteLittle(int x, byte[] out, int off) + { + out[off] = (byte)x; + out[off + 1] = (byte)(x >>> 8); + out[off + 2] = (byte)(x >>> 16); + out[off + 3] = (byte)(x >>> 24); + return out; + } + + /** + * Rotate left + * + * @param x value to rotate + * @param y amount to rotate x + * + * @return rotated x + */ + private int rotl(int x, int y) + { + return (x << y) | (x >>> -y); + } + + /** + * Pack byte[] array into an int in little endian order + * + * @param x byte array to 'pack' + * @param offset only x[offset]..x[offset+3] will be packed + * + * @return x[offset]..x[offset+3] 'packed' into an int in little-endian order + */ + private int byteToIntLittle(byte[] x, int offset) + { + return ((x[offset] & 255)) | + ((x[offset + 1] & 255) << 8) | + ((x[offset + 2] & 255) << 16) | + (x[offset + 3] << 24); + } + + private void resetCounter() + { + cW0 = 0; + cW1 = 0; + cW2 = 0; + } + + private boolean limitExceeded() + { + cW0++; + if (cW0 == 0) + { + cW1++; + if (cW1 == 0) + { + cW2++; + return (cW2 & 0x20) != 0; // 2^(32 + 32 + 6) + } + } + + return false; + } + + /* + * this relies on the fact len will always be positive. + */ + private boolean limitExceeded(int len) + { + if (cW0 >= 0) + { + cW0 += len; + } + else + { + cW0 += len; + if (cW0 >= 0) + { + cW1++; + if (cW1 == 0) + { + cW2++; + return (cW2 & 0x20) != 0; // 2^(32 + 32 + 6) + } + } + } + + return false; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/engines/TwofishEngine.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/engines/TwofishEngine.java new file mode 100644 index 00000000..adb908eb --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/engines/TwofishEngine.java @@ -0,0 +1,677 @@ +package org.bouncycastle.crypto.engines; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.params.KeyParameter; + +/** + * A class that provides Twofish encryption operations. + * + * This Java implementation is based on the Java reference + * implementation provided by Bruce Schneier and developed + * by Raif S. Naffah. + */ +public final class TwofishEngine + implements BlockCipher +{ + private static final byte[][] P = { + { // p0 + (byte) 0xA9, (byte) 0x67, (byte) 0xB3, (byte) 0xE8, + (byte) 0x04, (byte) 0xFD, (byte) 0xA3, (byte) 0x76, + (byte) 0x9A, (byte) 0x92, (byte) 0x80, (byte) 0x78, + (byte) 0xE4, (byte) 0xDD, (byte) 0xD1, (byte) 0x38, + (byte) 0x0D, (byte) 0xC6, (byte) 0x35, (byte) 0x98, + (byte) 0x18, (byte) 0xF7, (byte) 0xEC, (byte) 0x6C, + (byte) 0x43, (byte) 0x75, (byte) 0x37, (byte) 0x26, + (byte) 0xFA, (byte) 0x13, (byte) 0x94, (byte) 0x48, + (byte) 0xF2, (byte) 0xD0, (byte) 0x8B, (byte) 0x30, + (byte) 0x84, (byte) 0x54, (byte) 0xDF, (byte) 0x23, + (byte) 0x19, (byte) 0x5B, (byte) 0x3D, (byte) 0x59, + (byte) 0xF3, (byte) 0xAE, (byte) 0xA2, (byte) 0x82, + (byte) 0x63, (byte) 0x01, (byte) 0x83, (byte) 0x2E, + (byte) 0xD9, (byte) 0x51, (byte) 0x9B, (byte) 0x7C, + (byte) 0xA6, (byte) 0xEB, (byte) 0xA5, (byte) 0xBE, + (byte) 0x16, (byte) 0x0C, (byte) 0xE3, (byte) 0x61, + (byte) 0xC0, (byte) 0x8C, (byte) 0x3A, (byte) 0xF5, + (byte) 0x73, (byte) 0x2C, (byte) 0x25, (byte) 0x0B, + (byte) 0xBB, (byte) 0x4E, (byte) 0x89, (byte) 0x6B, + (byte) 0x53, (byte) 0x6A, (byte) 0xB4, (byte) 0xF1, + (byte) 0xE1, (byte) 0xE6, (byte) 0xBD, (byte) 0x45, + (byte) 0xE2, (byte) 0xF4, (byte) 0xB6, (byte) 0x66, + (byte) 0xCC, (byte) 0x95, (byte) 0x03, (byte) 0x56, + (byte) 0xD4, (byte) 0x1C, (byte) 0x1E, (byte) 0xD7, + (byte) 0xFB, (byte) 0xC3, (byte) 0x8E, (byte) 0xB5, + (byte) 0xE9, (byte) 0xCF, (byte) 0xBF, (byte) 0xBA, + (byte) 0xEA, (byte) 0x77, (byte) 0x39, (byte) 0xAF, + (byte) 0x33, (byte) 0xC9, (byte) 0x62, (byte) 0x71, + (byte) 0x81, (byte) 0x79, (byte) 0x09, (byte) 0xAD, + (byte) 0x24, (byte) 0xCD, (byte) 0xF9, (byte) 0xD8, + (byte) 0xE5, (byte) 0xC5, (byte) 0xB9, (byte) 0x4D, + (byte) 0x44, (byte) 0x08, (byte) 0x86, (byte) 0xE7, + (byte) 0xA1, (byte) 0x1D, (byte) 0xAA, (byte) 0xED, + (byte) 0x06, (byte) 0x70, (byte) 0xB2, (byte) 0xD2, + (byte) 0x41, (byte) 0x7B, (byte) 0xA0, (byte) 0x11, + (byte) 0x31, (byte) 0xC2, (byte) 0x27, (byte) 0x90, + (byte) 0x20, (byte) 0xF6, (byte) 0x60, (byte) 0xFF, + (byte) 0x96, (byte) 0x5C, (byte) 0xB1, (byte) 0xAB, + (byte) 0x9E, (byte) 0x9C, (byte) 0x52, (byte) 0x1B, + (byte) 0x5F, (byte) 0x93, (byte) 0x0A, (byte) 0xEF, + (byte) 0x91, (byte) 0x85, (byte) 0x49, (byte) 0xEE, + (byte) 0x2D, (byte) 0x4F, (byte) 0x8F, (byte) 0x3B, + (byte) 0x47, (byte) 0x87, (byte) 0x6D, (byte) 0x46, + (byte) 0xD6, (byte) 0x3E, (byte) 0x69, (byte) 0x64, + (byte) 0x2A, (byte) 0xCE, (byte) 0xCB, (byte) 0x2F, + (byte) 0xFC, (byte) 0x97, (byte) 0x05, (byte) 0x7A, + (byte) 0xAC, (byte) 0x7F, (byte) 0xD5, (byte) 0x1A, + (byte) 0x4B, (byte) 0x0E, (byte) 0xA7, (byte) 0x5A, + (byte) 0x28, (byte) 0x14, (byte) 0x3F, (byte) 0x29, + (byte) 0x88, (byte) 0x3C, (byte) 0x4C, (byte) 0x02, + (byte) 0xB8, (byte) 0xDA, (byte) 0xB0, (byte) 0x17, + (byte) 0x55, (byte) 0x1F, (byte) 0x8A, (byte) 0x7D, + (byte) 0x57, (byte) 0xC7, (byte) 0x8D, (byte) 0x74, + (byte) 0xB7, (byte) 0xC4, (byte) 0x9F, (byte) 0x72, + (byte) 0x7E, (byte) 0x15, (byte) 0x22, (byte) 0x12, + (byte) 0x58, (byte) 0x07, (byte) 0x99, (byte) 0x34, + (byte) 0x6E, (byte) 0x50, (byte) 0xDE, (byte) 0x68, + (byte) 0x65, (byte) 0xBC, (byte) 0xDB, (byte) 0xF8, + (byte) 0xC8, (byte) 0xA8, (byte) 0x2B, (byte) 0x40, + (byte) 0xDC, (byte) 0xFE, (byte) 0x32, (byte) 0xA4, + (byte) 0xCA, (byte) 0x10, (byte) 0x21, (byte) 0xF0, + (byte) 0xD3, (byte) 0x5D, (byte) 0x0F, (byte) 0x00, + (byte) 0x6F, (byte) 0x9D, (byte) 0x36, (byte) 0x42, + (byte) 0x4A, (byte) 0x5E, (byte) 0xC1, (byte) 0xE0 }, + { // p1 + (byte) 0x75, (byte) 0xF3, (byte) 0xC6, (byte) 0xF4, + (byte) 0xDB, (byte) 0x7B, (byte) 0xFB, (byte) 0xC8, + (byte) 0x4A, (byte) 0xD3, (byte) 0xE6, (byte) 0x6B, + (byte) 0x45, (byte) 0x7D, (byte) 0xE8, (byte) 0x4B, + (byte) 0xD6, (byte) 0x32, (byte) 0xD8, (byte) 0xFD, + (byte) 0x37, (byte) 0x71, (byte) 0xF1, (byte) 0xE1, + (byte) 0x30, (byte) 0x0F, (byte) 0xF8, (byte) 0x1B, + (byte) 0x87, (byte) 0xFA, (byte) 0x06, (byte) 0x3F, + (byte) 0x5E, (byte) 0xBA, (byte) 0xAE, (byte) 0x5B, + (byte) 0x8A, (byte) 0x00, (byte) 0xBC, (byte) 0x9D, + (byte) 0x6D, (byte) 0xC1, (byte) 0xB1, (byte) 0x0E, + (byte) 0x80, (byte) 0x5D, (byte) 0xD2, (byte) 0xD5, + (byte) 0xA0, (byte) 0x84, (byte) 0x07, (byte) 0x14, + (byte) 0xB5, (byte) 0x90, (byte) 0x2C, (byte) 0xA3, + (byte) 0xB2, (byte) 0x73, (byte) 0x4C, (byte) 0x54, + (byte) 0x92, (byte) 0x74, (byte) 0x36, (byte) 0x51, + (byte) 0x38, (byte) 0xB0, (byte) 0xBD, (byte) 0x5A, + (byte) 0xFC, (byte) 0x60, (byte) 0x62, (byte) 0x96, + (byte) 0x6C, (byte) 0x42, (byte) 0xF7, (byte) 0x10, + (byte) 0x7C, (byte) 0x28, (byte) 0x27, (byte) 0x8C, + (byte) 0x13, (byte) 0x95, (byte) 0x9C, (byte) 0xC7, + (byte) 0x24, (byte) 0x46, (byte) 0x3B, (byte) 0x70, + (byte) 0xCA, (byte) 0xE3, (byte) 0x85, (byte) 0xCB, + (byte) 0x11, (byte) 0xD0, (byte) 0x93, (byte) 0xB8, + (byte) 0xA6, (byte) 0x83, (byte) 0x20, (byte) 0xFF, + (byte) 0x9F, (byte) 0x77, (byte) 0xC3, (byte) 0xCC, + (byte) 0x03, (byte) 0x6F, (byte) 0x08, (byte) 0xBF, + (byte) 0x40, (byte) 0xE7, (byte) 0x2B, (byte) 0xE2, + (byte) 0x79, (byte) 0x0C, (byte) 0xAA, (byte) 0x82, + (byte) 0x41, (byte) 0x3A, (byte) 0xEA, (byte) 0xB9, + (byte) 0xE4, (byte) 0x9A, (byte) 0xA4, (byte) 0x97, + (byte) 0x7E, (byte) 0xDA, (byte) 0x7A, (byte) 0x17, + (byte) 0x66, (byte) 0x94, (byte) 0xA1, (byte) 0x1D, + (byte) 0x3D, (byte) 0xF0, (byte) 0xDE, (byte) 0xB3, + (byte) 0x0B, (byte) 0x72, (byte) 0xA7, (byte) 0x1C, + (byte) 0xEF, (byte) 0xD1, (byte) 0x53, (byte) 0x3E, + (byte) 0x8F, (byte) 0x33, (byte) 0x26, (byte) 0x5F, + (byte) 0xEC, (byte) 0x76, (byte) 0x2A, (byte) 0x49, + (byte) 0x81, (byte) 0x88, (byte) 0xEE, (byte) 0x21, + (byte) 0xC4, (byte) 0x1A, (byte) 0xEB, (byte) 0xD9, + (byte) 0xC5, (byte) 0x39, (byte) 0x99, (byte) 0xCD, + (byte) 0xAD, (byte) 0x31, (byte) 0x8B, (byte) 0x01, + (byte) 0x18, (byte) 0x23, (byte) 0xDD, (byte) 0x1F, + (byte) 0x4E, (byte) 0x2D, (byte) 0xF9, (byte) 0x48, + (byte) 0x4F, (byte) 0xF2, (byte) 0x65, (byte) 0x8E, + (byte) 0x78, (byte) 0x5C, (byte) 0x58, (byte) 0x19, + (byte) 0x8D, (byte) 0xE5, (byte) 0x98, (byte) 0x57, + (byte) 0x67, (byte) 0x7F, (byte) 0x05, (byte) 0x64, + (byte) 0xAF, (byte) 0x63, (byte) 0xB6, (byte) 0xFE, + (byte) 0xF5, (byte) 0xB7, (byte) 0x3C, (byte) 0xA5, + (byte) 0xCE, (byte) 0xE9, (byte) 0x68, (byte) 0x44, + (byte) 0xE0, (byte) 0x4D, (byte) 0x43, (byte) 0x69, + (byte) 0x29, (byte) 0x2E, (byte) 0xAC, (byte) 0x15, + (byte) 0x59, (byte) 0xA8, (byte) 0x0A, (byte) 0x9E, + (byte) 0x6E, (byte) 0x47, (byte) 0xDF, (byte) 0x34, + (byte) 0x35, (byte) 0x6A, (byte) 0xCF, (byte) 0xDC, + (byte) 0x22, (byte) 0xC9, (byte) 0xC0, (byte) 0x9B, + (byte) 0x89, (byte) 0xD4, (byte) 0xED, (byte) 0xAB, + (byte) 0x12, (byte) 0xA2, (byte) 0x0D, (byte) 0x52, + (byte) 0xBB, (byte) 0x02, (byte) 0x2F, (byte) 0xA9, + (byte) 0xD7, (byte) 0x61, (byte) 0x1E, (byte) 0xB4, + (byte) 0x50, (byte) 0x04, (byte) 0xF6, (byte) 0xC2, + (byte) 0x16, (byte) 0x25, (byte) 0x86, (byte) 0x56, + (byte) 0x55, (byte) 0x09, (byte) 0xBE, (byte) 0x91 } + }; + + /** + * Define the fixed p0/p1 permutations used in keyed S-box lookup. + * By changing the following constant definitions, the S-boxes will + * automatically get changed in the Twofish engine. + */ + private static final int P_00 = 1; + private static final int P_01 = 0; + private static final int P_02 = 0; + private static final int P_03 = P_01 ^ 1; + private static final int P_04 = 1; + + private static final int P_10 = 0; + private static final int P_11 = 0; + private static final int P_12 = 1; + private static final int P_13 = P_11 ^ 1; + private static final int P_14 = 0; + + private static final int P_20 = 1; + private static final int P_21 = 1; + private static final int P_22 = 0; + private static final int P_23 = P_21 ^ 1; + private static final int P_24 = 0; + + private static final int P_30 = 0; + private static final int P_31 = 1; + private static final int P_32 = 1; + private static final int P_33 = P_31 ^ 1; + private static final int P_34 = 1; + + /* Primitive polynomial for GF(256) */ + private static final int GF256_FDBK = 0x169; + private static final int GF256_FDBK_2 = GF256_FDBK / 2; + private static final int GF256_FDBK_4 = GF256_FDBK / 4; + + private static final int RS_GF_FDBK = 0x14D; // field generator + + //==================================== + // Useful constants + //==================================== + + private static final int ROUNDS = 16; + private static final int MAX_ROUNDS = 16; // bytes = 128 bits + private static final int BLOCK_SIZE = 16; // bytes = 128 bits + private static final int MAX_KEY_BITS = 256; + + private static final int INPUT_WHITEN=0; + private static final int OUTPUT_WHITEN=INPUT_WHITEN+BLOCK_SIZE/4; // 4 + private static final int ROUND_SUBKEYS=OUTPUT_WHITEN+BLOCK_SIZE/4;// 8 + + private static final int TOTAL_SUBKEYS=ROUND_SUBKEYS+2*MAX_ROUNDS;// 40 + + private static final int SK_STEP = 0x02020202; + private static final int SK_BUMP = 0x01010101; + private static final int SK_ROTL = 9; + + private boolean encrypting = false; + + private int[] gMDS0 = new int[MAX_KEY_BITS]; + private int[] gMDS1 = new int[MAX_KEY_BITS]; + private int[] gMDS2 = new int[MAX_KEY_BITS]; + private int[] gMDS3 = new int[MAX_KEY_BITS]; + + /** + * gSubKeys[] and gSBox[] are eventually used in the + * encryption and decryption methods. + */ + private int[] gSubKeys; + private int[] gSBox; + + private int k64Cnt = 0; + + private byte[] workingKey = null; + + public TwofishEngine() + { + // calculate the MDS matrix + int[] m1 = new int[2]; + int[] mX = new int[2]; + int[] mY = new int[2]; + int j; + + for (int i=0; i< MAX_KEY_BITS ; i++) + { + j = P[0][i] & 0xff; + m1[0] = j; + mX[0] = Mx_X(j) & 0xff; + mY[0] = Mx_Y(j) & 0xff; + + j = P[1][i] & 0xff; + m1[1] = j; + mX[1] = Mx_X(j) & 0xff; + mY[1] = Mx_Y(j) & 0xff; + + gMDS0[i] = m1[P_00] | mX[P_00] << 8 | + mY[P_00] << 16 | mY[P_00] << 24; + + gMDS1[i] = mY[P_10] | mY[P_10] << 8 | + mX[P_10] << 16 | m1[P_10] << 24; + + gMDS2[i] = mX[P_20] | mY[P_20] << 8 | + m1[P_20] << 16 | mY[P_20] << 24; + + gMDS3[i] = mX[P_30] | m1[P_30] << 8 | + mY[P_30] << 16 | mX[P_30] << 24; + } + } + + /** + * initialise a Twofish cipher. + * + * @param encrypting whether or not we are for encryption. + * @param params the parameters required to set up the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean encrypting, + CipherParameters params) + { + if (params instanceof KeyParameter) + { + this.encrypting = encrypting; + this.workingKey = ((KeyParameter)params).getKey(); + this.k64Cnt = (this.workingKey.length / 8); // pre-padded ? + setKey(this.workingKey); + + return; + } + + throw new IllegalArgumentException("invalid parameter passed to Twofish init - " + params.getClass().getName()); + } + + public String getAlgorithmName() + { + return "Twofish"; + } + + public int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + { + if (workingKey == null) + { + throw new IllegalStateException("Twofish not initialised"); + } + + if ((inOff + BLOCK_SIZE) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + BLOCK_SIZE) > out.length) + { + throw new DataLengthException("output buffer too short"); + } + + if (encrypting) + { + encryptBlock(in, inOff, out, outOff); + } + else + { + decryptBlock(in, inOff, out, outOff); + } + + return BLOCK_SIZE; + } + + public void reset() + { + if (this.workingKey != null) + { + setKey(this.workingKey); + } + } + + public int getBlockSize() + { + return BLOCK_SIZE; + } + + //================================== + // Private Implementation + //================================== + + private void setKey(byte[] key) + { + int[] k32e = new int[MAX_KEY_BITS/64]; // 4 + int[] k32o = new int[MAX_KEY_BITS/64]; // 4 + + int[] sBoxKeys = new int[MAX_KEY_BITS/64]; // 4 + gSubKeys = new int[TOTAL_SUBKEYS]; + + if (k64Cnt < 1) + { + throw new IllegalArgumentException("Key size less than 64 bits"); + } + + if (k64Cnt > 4) + { + throw new IllegalArgumentException("Key size larger than 256 bits"); + } + + /* + * k64Cnt is the number of 8 byte blocks (64 chunks) + * that are in the input key. The input key is a + * maximum of 32 bytes (256 bits), so the range + * for k64Cnt is 1..4 + */ + for (int i=0; i>> 24; + A += B; + gSubKeys[i*2] = A; + A += B; + gSubKeys[i*2 + 1] = A << SK_ROTL | A >>> (32-SK_ROTL); + } + + /* + * fully expand the table for speed + */ + int k0 = sBoxKeys[0]; + int k1 = sBoxKeys[1]; + int k2 = sBoxKeys[2]; + int k3 = sBoxKeys[3]; + int b0, b1, b2, b3; + gSBox = new int[4*MAX_KEY_BITS]; + for (int i=0; i>>1 | x2 << 31; + x3 = (x3 << 1 | x3 >>> 31) ^ (t0 + 2*t1 + gSubKeys[k++]); + + t0 = Fe32_0(x2); + t1 = Fe32_3(x3); + x0 ^= t0 + t1 + gSubKeys[k++]; + x0 = x0 >>>1 | x0 << 31; + x1 = (x1 << 1 | x1 >>> 31) ^ (t0 + 2*t1 + gSubKeys[k++]); + } + + Bits32ToBytes(x2 ^ gSubKeys[OUTPUT_WHITEN], dst, dstIndex); + Bits32ToBytes(x3 ^ gSubKeys[OUTPUT_WHITEN + 1], dst, dstIndex + 4); + Bits32ToBytes(x0 ^ gSubKeys[OUTPUT_WHITEN + 2], dst, dstIndex + 8); + Bits32ToBytes(x1 ^ gSubKeys[OUTPUT_WHITEN + 3], dst, dstIndex + 12); + } + + /** + * Decrypt the given input starting at the given offset and place + * the result in the provided buffer starting at the given offset. + * The input will be an exact multiple of our blocksize. + */ + private void decryptBlock( + byte[] src, + int srcIndex, + byte[] dst, + int dstIndex) + { + int x2 = BytesTo32Bits(src, srcIndex) ^ gSubKeys[OUTPUT_WHITEN]; + int x3 = BytesTo32Bits(src, srcIndex+4) ^ gSubKeys[OUTPUT_WHITEN + 1]; + int x0 = BytesTo32Bits(src, srcIndex+8) ^ gSubKeys[OUTPUT_WHITEN + 2]; + int x1 = BytesTo32Bits(src, srcIndex+12) ^ gSubKeys[OUTPUT_WHITEN + 3]; + + int k = ROUND_SUBKEYS + 2 * ROUNDS -1 ; + int t0, t1; + for (int r = 0; r< ROUNDS ; r +=2) + { + t0 = Fe32_0(x2); + t1 = Fe32_3(x3); + x1 ^= t0 + 2*t1 + gSubKeys[k--]; + x0 = (x0 << 1 | x0 >>> 31) ^ (t0 + t1 + gSubKeys[k--]); + x1 = x1 >>>1 | x1 << 31; + + t0 = Fe32_0(x0); + t1 = Fe32_3(x1); + x3 ^= t0 + 2*t1 + gSubKeys[k--]; + x2 = (x2 << 1 | x2 >>> 31) ^ (t0 + t1 + gSubKeys[k--]); + x3 = x3 >>>1 | x3 << 31; + } + + Bits32ToBytes(x0 ^ gSubKeys[INPUT_WHITEN], dst, dstIndex); + Bits32ToBytes(x1 ^ gSubKeys[INPUT_WHITEN + 1], dst, dstIndex + 4); + Bits32ToBytes(x2 ^ gSubKeys[INPUT_WHITEN + 2], dst, dstIndex + 8); + Bits32ToBytes(x3 ^ gSubKeys[INPUT_WHITEN + 3], dst, dstIndex + 12); + } + + /* + * TODO: This can be optimised and made cleaner by combining + * the functionality in this function and applying it appropriately + * to the creation of the subkeys during key setup. + */ + private int F32(int x, int[] k32) + { + int b0 = b0(x); + int b1 = b1(x); + int b2 = b2(x); + int b3 = b3(x); + int k0 = k32[0]; + int k1 = k32[1]; + int k2 = k32[2]; + int k3 = k32[3]; + + int result = 0; + switch (k64Cnt & 3) + { + case 1: + result = gMDS0[(P[P_01][b0] & 0xff) ^ b0(k0)] ^ + gMDS1[(P[P_11][b1] & 0xff) ^ b1(k0)] ^ + gMDS2[(P[P_21][b2] & 0xff) ^ b2(k0)] ^ + gMDS3[(P[P_31][b3] & 0xff) ^ b3(k0)]; + break; + case 0: /* 256 bits of key */ + b0 = (P[P_04][b0] & 0xff) ^ b0(k3); + b1 = (P[P_14][b1] & 0xff) ^ b1(k3); + b2 = (P[P_24][b2] & 0xff) ^ b2(k3); + b3 = (P[P_34][b3] & 0xff) ^ b3(k3); + case 3: + b0 = (P[P_03][b0] & 0xff) ^ b0(k2); + b1 = (P[P_13][b1] & 0xff) ^ b1(k2); + b2 = (P[P_23][b2] & 0xff) ^ b2(k2); + b3 = (P[P_33][b3] & 0xff) ^ b3(k2); + case 2: + result = + gMDS0[(P[P_01][(P[P_02][b0]&0xff)^b0(k1)]&0xff)^b0(k0)] ^ + gMDS1[(P[P_11][(P[P_12][b1]&0xff)^b1(k1)]&0xff)^b1(k0)] ^ + gMDS2[(P[P_21][(P[P_22][b2]&0xff)^b2(k1)]&0xff)^b2(k0)] ^ + gMDS3[(P[P_31][(P[P_32][b3]&0xff)^b3(k1)]&0xff)^b3(k0)]; + break; + } + return result; + } + + /** + * Use (12, 8) Reed-Solomon code over GF(256) to produce + * a key S-box 32-bit entity from 2 key material 32-bit + * entities. + * + * @param k0 first 32-bit entity + * @param k1 second 32-bit entity + * @return Remainder polynomial generated using RS code + */ + private int RS_MDS_Encode(int k0, int k1) + { + int r = k1; + for (int i = 0 ; i < 4 ; i++) // shift 1 byte at a time + { + r = RS_rem(r); + } + r ^= k0; + for (int i=0 ; i < 4 ; i++) + { + r = RS_rem(r); + } + + return r; + } + + /** + * Reed-Solomon code parameters: (12,8) reversible code:

+ *

+     * g(x) = x^4 + (a+1/a)x^3 + ax^2 + (a+1/a)x + 1
+     * 
+ * where a = primitive root of field generator 0x14D + */ + private int RS_rem(int x) + { + int b = (x >>> 24) & 0xff; + int g2 = ((b << 1) ^ + ((b & 0x80) != 0 ? RS_GF_FDBK : 0)) & 0xff; + int g3 = ((b >>> 1) ^ + ((b & 0x01) != 0 ? (RS_GF_FDBK >>> 1) : 0)) ^ g2 ; + return ((x << 8) ^ (g3 << 24) ^ (g2 << 16) ^ (g3 << 8) ^ b); + } + + private int LFSR1(int x) + { + return (x >> 1) ^ + (((x & 0x01) != 0) ? GF256_FDBK_2 : 0); + } + + private int LFSR2(int x) + { + return (x >> 2) ^ + (((x & 0x02) != 0) ? GF256_FDBK_2 : 0) ^ + (((x & 0x01) != 0) ? GF256_FDBK_4 : 0); + } + + private int Mx_X(int x) + { + return x ^ LFSR2(x); + } // 5B + + private int Mx_Y(int x) + { + return x ^ LFSR1(x) ^ LFSR2(x); + } // EF + + private int b0(int x) + { + return x & 0xff; + } + + private int b1(int x) + { + return (x >>> 8) & 0xff; + } + + private int b2(int x) + { + return (x >>> 16) & 0xff; + } + + private int b3(int x) + { + return (x >>> 24) & 0xff; + } + + private int Fe32_0(int x) + { + return gSBox[ 0x000 + 2*(x & 0xff) ] ^ + gSBox[ 0x001 + 2*((x >>> 8) & 0xff) ] ^ + gSBox[ 0x200 + 2*((x >>> 16) & 0xff) ] ^ + gSBox[ 0x201 + 2*((x >>> 24) & 0xff) ]; + } + + private int Fe32_3(int x) + { + return gSBox[ 0x000 + 2*((x >>> 24) & 0xff) ] ^ + gSBox[ 0x001 + 2*(x & 0xff) ] ^ + gSBox[ 0x200 + 2*((x >>> 8) & 0xff) ] ^ + gSBox[ 0x201 + 2*((x >>> 16) & 0xff) ]; + } + + private int BytesTo32Bits(byte[] b, int p) + { + return ((b[p] & 0xff)) | + ((b[p+1] & 0xff) << 8) | + ((b[p+2] & 0xff) << 16) | + ((b[p+3] & 0xff) << 24); + } + + private void Bits32ToBytes(int in, byte[] b, int offset) + { + b[offset] = (byte)in; + b[offset + 1] = (byte)(in >> 8); + b[offset + 2] = (byte)(in >> 16); + b[offset + 3] = (byte)(in >> 24); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java new file mode 100644 index 00000000..8a4d28ab --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/generators/OpenSSLPBEParametersGenerator.java @@ -0,0 +1,131 @@ +package org.bouncycastle.crypto.generators; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.PBEParametersGenerator; +import org.bouncycastle.crypto.digests.MD5Digest; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; + +/** + * Generator for PBE derived keys and ivs as usd by OpenSSL. + *

+ * The scheme is a simple extension of PKCS 5 V2.0 Scheme 1 using MD5 with an + * iteration count of 1. + *

+ */ +public class OpenSSLPBEParametersGenerator + extends PBEParametersGenerator +{ + private Digest digest = new MD5Digest(); + + /** + * Construct a OpenSSL Parameters generator. + */ + public OpenSSLPBEParametersGenerator() + { + } + + /** + * Initialise - note the iteration count for this algorithm is fixed at 1. + * + * @param password password to use. + * @param salt salt to use. + */ + public void init( + byte[] password, + byte[] salt) + { + super.init(password, salt, 1); + } + + /** + * the derived key function, the ith hash of the password and the salt. + */ + private byte[] generateDerivedKey( + int bytesNeeded) + { + byte[] buf = new byte[digest.getDigestSize()]; + byte[] key = new byte[bytesNeeded]; + int offset = 0; + + for (;;) + { + digest.update(password, 0, password.length); + digest.update(salt, 0, salt.length); + + digest.doFinal(buf, 0); + + int len = (bytesNeeded > buf.length) ? buf.length : bytesNeeded; + System.arraycopy(buf, 0, key, offset, len); + offset += len; + + // check if we need any more + bytesNeeded -= len; + if (bytesNeeded == 0) + { + break; + } + + // do another round + digest.reset(); + digest.update(buf, 0, buf.length); + } + + return key; + } + + /** + * Generate a key parameter derived from the password, salt, and iteration + * count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + * @exception IllegalArgumentException if the key length larger than the base hash size. + */ + public CipherParameters generateDerivedParameters( + int keySize) + { + keySize = keySize / 8; + + byte[] dKey = generateDerivedKey(keySize); + + return new KeyParameter(dKey, 0, keySize); + } + + /** + * Generate a key with initialisation vector parameter derived from + * the password, salt, and iteration count we are currently initialised + * with. + * + * @param keySize the size of the key we want (in bits) + * @param ivSize the size of the iv we want (in bits) + * @return a ParametersWithIV object. + * @exception IllegalArgumentException if keySize + ivSize is larger than the base hash size. + */ + public CipherParameters generateDerivedParameters( + int keySize, + int ivSize) + { + keySize = keySize / 8; + ivSize = ivSize / 8; + + byte[] dKey = generateDerivedKey(keySize + ivSize); + + return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), dKey, keySize, ivSize); + } + + /** + * Generate a key parameter for use with a MAC derived from the password, + * salt, and iteration count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + * @exception IllegalArgumentException if the key length larger than the base hash size. + */ + public CipherParameters generateDerivedMacParameters( + int keySize) + { + return generateDerivedParameters(keySize); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/generators/PKCS12ParametersGenerator.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/generators/PKCS12ParametersGenerator.java new file mode 100644 index 00000000..bf2f3689 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/generators/PKCS12ParametersGenerator.java @@ -0,0 +1,221 @@ +package org.bouncycastle.crypto.generators; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.ExtendedDigest; +import org.bouncycastle.crypto.PBEParametersGenerator; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; + +/** + * Generator for PBE derived keys and ivs as defined by PKCS 12 V1.0. + *

+ * The document this implementation is based on can be found at + * + * RSA's PKCS12 Page + */ +public class PKCS12ParametersGenerator + extends PBEParametersGenerator +{ + public static final int KEY_MATERIAL = 1; + public static final int IV_MATERIAL = 2; + public static final int MAC_MATERIAL = 3; + + private Digest digest; + + private int u; + private int v; + + /** + * Construct a PKCS 12 Parameters generator. This constructor will + * accept any digest which also implements ExtendedDigest. + * + * @param digest the digest to be used as the source of derived keys. + * @exception IllegalArgumentException if an unknown digest is passed in. + */ + public PKCS12ParametersGenerator( + Digest digest) + { + this.digest = digest; + if (digest instanceof ExtendedDigest) + { + u = digest.getDigestSize(); + v = ((ExtendedDigest)digest).getByteLength(); + } + else + { + throw new IllegalArgumentException("Digest " + digest.getAlgorithmName() + " unsupported"); + } + } + + /** + * add a + b + 1, returning the result in a. The a value is treated + * as a BigInteger of length (b.length * 8) bits. The result is + * modulo 2^b.length in case of overflow. + */ + private void adjust( + byte[] a, + int aOff, + byte[] b) + { + int x = (b[b.length - 1] & 0xff) + (a[aOff + b.length - 1] & 0xff) + 1; + + a[aOff + b.length - 1] = (byte)x; + x >>>= 8; + + for (int i = b.length - 2; i >= 0; i--) + { + x += (b[i] & 0xff) + (a[aOff + i] & 0xff); + a[aOff + i] = (byte)x; + x >>>= 8; + } + } + + /** + * generation of a derived key ala PKCS12 V1.0. + */ + private byte[] generateDerivedKey( + int idByte, + int n) + { + byte[] D = new byte[v]; + byte[] dKey = new byte[n]; + + for (int i = 0; i != D.length; i++) + { + D[i] = (byte)idByte; + } + + byte[] S; + + if ((salt != null) && (salt.length != 0)) + { + S = new byte[v * ((salt.length + v - 1) / v)]; + + for (int i = 0; i != S.length; i++) + { + S[i] = salt[i % salt.length]; + } + } + else + { + S = new byte[0]; + } + + byte[] P; + + if ((password != null) && (password.length != 0)) + { + P = new byte[v * ((password.length + v - 1) / v)]; + + for (int i = 0; i != P.length; i++) + { + P[i] = password[i % password.length]; + } + } + else + { + P = new byte[0]; + } + + byte[] I = new byte[S.length + P.length]; + + System.arraycopy(S, 0, I, 0, S.length); + System.arraycopy(P, 0, I, S.length, P.length); + + byte[] B = new byte[v]; + int c = (n + u - 1) / u; + + for (int i = 1; i <= c; i++) + { + byte[] A = new byte[u]; + + digest.update(D, 0, D.length); + digest.update(I, 0, I.length); + digest.doFinal(A, 0); + for (int j = 1; j != iterationCount; j++) + { + digest.update(A, 0, A.length); + digest.doFinal(A, 0); + } + + for (int j = 0; j != B.length; j++) + { + B[j] = A[j % A.length]; + } + + for (int j = 0; j != I.length / v; j++) + { + adjust(I, j * v, B); + } + + if (i == c) + { + System.arraycopy(A, 0, dKey, (i - 1) * u, dKey.length - ((i - 1) * u)); + } + else + { + System.arraycopy(A, 0, dKey, (i - 1) * u, A.length); + } + } + + return dKey; + } + + /** + * Generate a key parameter derived from the password, salt, and iteration + * count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + */ + public CipherParameters generateDerivedParameters( + int keySize) + { + keySize = keySize / 8; + + byte[] dKey = generateDerivedKey(KEY_MATERIAL, keySize); + + return new KeyParameter(dKey, 0, keySize); + } + + /** + * Generate a key with initialisation vector parameter derived from + * the password, salt, and iteration count we are currently initialised + * with. + * + * @param keySize the size of the key we want (in bits) + * @param ivSize the size of the iv we want (in bits) + * @return a ParametersWithIV object. + */ + public CipherParameters generateDerivedParameters( + int keySize, + int ivSize) + { + keySize = keySize / 8; + ivSize = ivSize / 8; + + byte[] dKey = generateDerivedKey(KEY_MATERIAL, keySize); + + byte[] iv = generateDerivedKey(IV_MATERIAL, ivSize); + + return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), iv, 0, ivSize); + } + + /** + * Generate a key parameter for use with a MAC derived from the password, + * salt, and iteration count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + */ + public CipherParameters generateDerivedMacParameters( + int keySize) + { + keySize = keySize / 8; + + byte[] dKey = generateDerivedKey(MAC_MATERIAL, keySize); + + return new KeyParameter(dKey, 0, keySize); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/generators/PKCS5S1ParametersGenerator.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/generators/PKCS5S1ParametersGenerator.java new file mode 100644 index 00000000..1c62eccc --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/generators/PKCS5S1ParametersGenerator.java @@ -0,0 +1,119 @@ +package org.bouncycastle.crypto.generators; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.PBEParametersGenerator; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; + +/** + * Generator for PBE derived keys and ivs as defined by PKCS 5 V2.0 Scheme 1. + * Note this generator is limited to the size of the hash produced by the + * digest used to drive it. + *

+ * The document this implementation is based on can be found at + * + * RSA's PKCS5 Page + */ +public class PKCS5S1ParametersGenerator + extends PBEParametersGenerator +{ + private Digest digest; + + /** + * Construct a PKCS 5 Scheme 1 Parameters generator. + * + * @param digest the digest to be used as the source of derived keys. + */ + public PKCS5S1ParametersGenerator( + Digest digest) + { + this.digest = digest; + } + + /** + * the derived key function, the ith hash of the password and the salt. + */ + private byte[] generateDerivedKey() + { + byte[] digestBytes = new byte[digest.getDigestSize()]; + + digest.update(password, 0, password.length); + digest.update(salt, 0, salt.length); + + digest.doFinal(digestBytes, 0); + for (int i = 1; i < iterationCount; i++) + { + digest.update(digestBytes, 0, digestBytes.length); + digest.doFinal(digestBytes, 0); + } + + return digestBytes; + } + + /** + * Generate a key parameter derived from the password, salt, and iteration + * count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + * @exception IllegalArgumentException if the key length larger than the base hash size. + */ + public CipherParameters generateDerivedParameters( + int keySize) + { + keySize = keySize / 8; + + if (keySize > digest.getDigestSize()) + { + throw new IllegalArgumentException( + "Can't generate a derived key " + keySize + " bytes long."); + } + + byte[] dKey = generateDerivedKey(); + + return new KeyParameter(dKey, 0, keySize); + } + + /** + * Generate a key with initialisation vector parameter derived from + * the password, salt, and iteration count we are currently initialised + * with. + * + * @param keySize the size of the key we want (in bits) + * @param ivSize the size of the iv we want (in bits) + * @return a ParametersWithIV object. + * @exception IllegalArgumentException if keySize + ivSize is larger than the base hash size. + */ + public CipherParameters generateDerivedParameters( + int keySize, + int ivSize) + { + keySize = keySize / 8; + ivSize = ivSize / 8; + + if ((keySize + ivSize) > digest.getDigestSize()) + { + throw new IllegalArgumentException( + "Can't generate a derived key " + (keySize + ivSize) + " bytes long."); + } + + byte[] dKey = generateDerivedKey(); + + return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), dKey, keySize, ivSize); + } + + /** + * Generate a key parameter for use with a MAC derived from the password, + * salt, and iteration count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + * @exception IllegalArgumentException if the key length larger than the base hash size. + */ + public CipherParameters generateDerivedMacParameters( + int keySize) + { + return generateDerivedParameters(keySize); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java new file mode 100644 index 00000000..9b4972d5 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java @@ -0,0 +1,151 @@ +package org.bouncycastle.crypto.generators; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.PBEParametersGenerator; +import org.bouncycastle.crypto.digests.SHA1Digest; +import org.bouncycastle.crypto.macs.HMac; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; + +/** + * Generator for PBE derived keys and ivs as defined by PKCS 5 V2.0 Scheme 2. + * This generator uses a SHA-1 HMac as the calculation function. + *

+ * The document this implementation is based on can be found at + * + * RSA's PKCS5 Page + */ +public class PKCS5S2ParametersGenerator + extends PBEParametersGenerator +{ + private Mac hMac = new HMac(new SHA1Digest()); + + /** + * construct a PKCS5 Scheme 2 Parameters generator. + */ + public PKCS5S2ParametersGenerator() + { + } + + private void F( + byte[] P, + byte[] S, + int c, + byte[] iBuf, + byte[] out, + int outOff) + { + byte[] state = new byte[hMac.getMacSize()]; + CipherParameters param = new KeyParameter(P); + + hMac.init(param); + + if (S != null) + { + hMac.update(S, 0, S.length); + } + + hMac.update(iBuf, 0, iBuf.length); + + hMac.doFinal(state, 0); + + System.arraycopy(state, 0, out, outOff, state.length); + + if (c == 0) + { + throw new IllegalArgumentException("iteration count must be at least 1."); + } + + for (int count = 1; count < c; count++) + { + hMac.init(param); + hMac.update(state, 0, state.length); + hMac.doFinal(state, 0); + + for (int j = 0; j != state.length; j++) + { + out[outOff + j] ^= state[j]; + } + } + } + + private void intToOctet( + byte[] buf, + int i) + { + buf[0] = (byte)(i >>> 24); + buf[1] = (byte)(i >>> 16); + buf[2] = (byte)(i >>> 8); + buf[3] = (byte)i; + } + + private byte[] generateDerivedKey( + int dkLen) + { + int hLen = hMac.getMacSize(); + int l = (dkLen + hLen - 1) / hLen; + byte[] iBuf = new byte[4]; + byte[] out = new byte[l * hLen]; + + for (int i = 1; i <= l; i++) + { + intToOctet(iBuf, i); + + F(password, salt, iterationCount, iBuf, out, (i - 1) * hLen); + } + + return out; + } + + /** + * Generate a key parameter derived from the password, salt, and iteration + * count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + */ + public CipherParameters generateDerivedParameters( + int keySize) + { + keySize = keySize / 8; + + byte[] dKey = generateDerivedKey(keySize); + + return new KeyParameter(dKey, 0, keySize); + } + + /** + * Generate a key with initialisation vector parameter derived from + * the password, salt, and iteration count we are currently initialised + * with. + * + * @param keySize the size of the key we want (in bits) + * @param ivSize the size of the iv we want (in bits) + * @return a ParametersWithIV object. + */ + public CipherParameters generateDerivedParameters( + int keySize, + int ivSize) + { + keySize = keySize / 8; + ivSize = ivSize / 8; + + byte[] dKey = generateDerivedKey(keySize + ivSize); + + return new ParametersWithIV(new KeyParameter(dKey, 0, keySize), dKey, keySize, ivSize); + } + + /** + * Generate a key parameter for use with a MAC derived from the password, + * salt, and iteration count we are currently initialised with. + * + * @param keySize the size of the key we want (in bits) + * @return a KeyParameter object. + */ + public CipherParameters generateDerivedMacParameters( + int keySize) + { + return generateDerivedParameters(keySize); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/macs/HMac.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/macs/HMac.java new file mode 100644 index 00000000..7189426a --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/macs/HMac.java @@ -0,0 +1,200 @@ +package org.bouncycastle.crypto.macs; + +import java.util.Hashtable; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.Digest; +import org.bouncycastle.crypto.ExtendedDigest; +import org.bouncycastle.crypto.Mac; +import org.bouncycastle.crypto.params.KeyParameter; + +/** + * HMAC implementation based on RFC2104 + * + * H(K XOR opad, H(K XOR ipad, text)) + */ +@SuppressWarnings("unchecked") +public class HMac + implements Mac +{ + private final static byte IPAD = (byte)0x36; + private final static byte OPAD = (byte)0x5C; + + private Digest digest; + private int digestSize; + private int blockLength; + + private byte[] inputPad; + private byte[] outputPad; + + private static Hashtable blockLengths; + + static + { + blockLengths = new Hashtable(); + + blockLengths.put("GOST3411", Integer.valueOf(32)); + + blockLengths.put("MD2", Integer.valueOf(16)); + blockLengths.put("MD4", Integer.valueOf(64)); + blockLengths.put("MD5", Integer.valueOf(64)); + + blockLengths.put("RIPEMD128", Integer.valueOf(64)); + blockLengths.put("RIPEMD160", Integer.valueOf(64)); + + blockLengths.put("SHA-1", Integer.valueOf(64)); + blockLengths.put("SHA-224", Integer.valueOf(64)); + blockLengths.put("SHA-256", Integer.valueOf(64)); + blockLengths.put("SHA-384", Integer.valueOf(128)); + blockLengths.put("SHA-512", Integer.valueOf(128)); + + blockLengths.put("Tiger", Integer.valueOf(64)); + blockLengths.put("Whirlpool", Integer.valueOf(64)); + } + + private static int getByteLength( + Digest digest) + { + if (digest instanceof ExtendedDigest) + { + return ((ExtendedDigest)digest).getByteLength(); + } + + Integer b = (Integer)blockLengths.get(digest.getAlgorithmName()); + + if (b == null) + { + throw new IllegalArgumentException("unknown digest passed: " + digest.getAlgorithmName()); + } + + return b.intValue(); + } + + /** + * Base constructor for one of the standard digest algorithms that the + * byteLength of the algorithm is know for. + * + * @param digest the digest. + */ + public HMac( + Digest digest) + { + this(digest, getByteLength(digest)); + } + + private HMac( + Digest digest, + int byteLength) + { + this.digest = digest; + digestSize = digest.getDigestSize(); + + this.blockLength = byteLength; + + inputPad = new byte[blockLength]; + outputPad = new byte[blockLength]; + } + + public String getAlgorithmName() + { + return digest.getAlgorithmName() + "/HMAC"; + } + + public Digest getUnderlyingDigest() + { + return digest; + } + + public void init( + CipherParameters params) + { + digest.reset(); + + byte[] key = ((KeyParameter)params).getKey(); + + if (key.length > blockLength) + { + digest.update(key, 0, key.length); + digest.doFinal(inputPad, 0); + for (int i = digestSize; i < inputPad.length; i++) + { + inputPad[i] = 0; + } + } + else + { + System.arraycopy(key, 0, inputPad, 0, key.length); + for (int i = key.length; i < inputPad.length; i++) + { + inputPad[i] = 0; + } + } + + outputPad = new byte[inputPad.length]; + System.arraycopy(inputPad, 0, outputPad, 0, inputPad.length); + + for (int i = 0; i < inputPad.length; i++) + { + inputPad[i] ^= IPAD; + } + + for (int i = 0; i < outputPad.length; i++) + { + outputPad[i] ^= OPAD; + } + + digest.update(inputPad, 0, inputPad.length); + } + + public int getMacSize() + { + return digestSize; + } + + public void update( + byte in) + { + digest.update(in); + } + + public void update( + byte[] in, + int inOff, + int len) + { + digest.update(in, inOff, len); + } + + public int doFinal( + byte[] out, + int outOff) + { + byte[] tmp = new byte[digestSize]; + digest.doFinal(tmp, 0); + + digest.update(outputPad, 0, outputPad.length); + digest.update(tmp, 0, tmp.length); + + int len = digest.doFinal(out, outOff); + + reset(); + + return len; + } + + /** + * Reset the mac generator. + */ + public void reset() + { + /* + * reset the underlying digest. + */ + digest.reset(); + + /* + * reinitialize the digest. + */ + digest.update(inputPad, 0, inputPad.length); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/modes/AEADBlockCipher.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/modes/AEADBlockCipher.java new file mode 100644 index 00000000..3c3bf341 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/modes/AEADBlockCipher.java @@ -0,0 +1,108 @@ +package org.bouncycastle.crypto.modes; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.InvalidCipherTextException; + +/** + * A block cipher mode that includes authenticated encryption with a streaming mode and optional associated data. + * @see org.bouncycastle.crypto.params.AEADParameters + */ +public interface AEADBlockCipher +{ + /** + * initialise the underlying cipher. Parameter can either be an AEADParameters or a ParametersWithIV object. + * + * @param forEncryption true if we are setting up for encryption, false otherwise. + * @param params the necessary parameters for the underlying cipher to be initialised. + * @exception IllegalArgumentException if the params argument is inappropriate. + */ + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException; + + /** + * Return the name of the algorithm. + * + * @return the algorithm name. + */ + public String getAlgorithmName(); + + /** + * return the cipher this object wraps. + * + * @return the cipher this object wraps. + */ + public BlockCipher getUnderlyingCipher(); + + /** + * encrypt/decrypt a single byte. + * + * @param in the byte to be processed. + * @param out the output buffer the processed byte goes into. + * @param outOff the offset into the output byte array the processed data starts at. + * @return the number of bytes written to out. + * @exception DataLengthException if the output buffer is too small. + */ + public int processByte(byte in, byte[] out, int outOff) + throws DataLengthException; + + /** + * process a block of bytes from in putting the result into out. + * + * @param in the input byte array. + * @param inOff the offset into the in array where the data to be processed starts. + * @param len the number of bytes to be processed. + * @param out the output buffer the processed bytes go into. + * @param outOff the offset into the output byte array the processed data starts at. + * @return the number of bytes written to out. + * @exception DataLengthException if the output buffer is too small. + */ + public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) + throws DataLengthException; + + /** + * Finish the operation either appending or verifying the MAC at the end of the data. + * + * @param out space for any resulting output data. + * @param outOff offset into out to start copying the data at. + * @return number of bytes written into out. + * @throws IllegalStateException if the cipher is in an inappropriate state. + * @throws org.bouncycastle.crypto.InvalidCipherTextException if the MAC fails to match. + */ + public int doFinal(byte[] out, int outOff) + throws IllegalStateException, InvalidCipherTextException; + + /** + * Return the value of the MAC associated with the last stream processed. + * + * @return MAC for plaintext data. + */ + public byte[] getMac(); + + /** + * return the size of the output buffer required for a processBytes + * an input of len bytes. + * + * @param len the length of the input. + * @return the space required to accommodate a call to processBytes + * with len bytes of input. + */ + public int getUpdateOutputSize(int len); + + /** + * return the size of the output buffer required for a processBytes plus a + * doFinal with an input of len bytes. + * + * @param len the length of the input. + * @return the space required to accommodate a call to processBytes and doFinal + * with len bytes of input. + */ + public int getOutputSize(int len); + + /** + * Reset the cipher. After resetting the cipher is in the same state + * as it was after the last init (if there was one). + */ + public void reset(); +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/modes/CBCBlockCipher.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/modes/CBCBlockCipher.java new file mode 100644 index 00000000..f75c5463 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/modes/CBCBlockCipher.java @@ -0,0 +1,235 @@ +package org.bouncycastle.crypto.modes; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.util.Arrays; + +/** + * implements Cipher-Block-Chaining (CBC) mode on top of a simple cipher. + */ +public class CBCBlockCipher + implements BlockCipher +{ + private byte[] IV; + private byte[] cbcV; + private byte[] cbcNextV; + + private int blockSize; + private BlockCipher cipher = null; + private boolean encrypting; + + /** + * Basic constructor. + * + * @param cipher the block cipher to be used as the basis of chaining. + */ + public CBCBlockCipher( + BlockCipher cipher) + { + this.cipher = cipher; + this.blockSize = cipher.getBlockSize(); + + this.IV = new byte[blockSize]; + this.cbcV = new byte[blockSize]; + this.cbcNextV = new byte[blockSize]; + } + + /** + * return the underlying block cipher that we are wrapping. + * + * @return the underlying block cipher that we are wrapping. + */ + public BlockCipher getUnderlyingCipher() + { + return cipher; + } + + /** + * Initialise the cipher and, possibly, the initialisation vector (IV). + * If an IV isn't passed as part of the parameter, the IV will be all zeros. + * + * @param encrypting if true the cipher is initialised for + * encryption, if false for decryption. + * @param params the key and other data required by the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean encrypting, + CipherParameters params) + throws IllegalArgumentException + { + this.encrypting = encrypting; + + if (params instanceof ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV)params; + byte[] iv = ivParam.getIV(); + + if (iv.length != blockSize) + { + throw new IllegalArgumentException("initialisation vector must be the same length as block size"); + } + + System.arraycopy(iv, 0, IV, 0, iv.length); + + reset(); + + cipher.init(encrypting, ivParam.getParameters()); + } + else + { + reset(); + + cipher.init(encrypting, params); + } + } + + /** + * return the algorithm name and mode. + * + * @return the name of the underlying algorithm followed by "/CBC". + */ + public String getAlgorithmName() + { + return cipher.getAlgorithmName() + "/CBC"; + } + + /** + * return the block size of the underlying cipher. + * + * @return the block size of the underlying cipher. + */ + public int getBlockSize() + { + return cipher.getBlockSize(); + } + + /** + * Process one block of input from the array in and write it to + * the out array. + * + * @param in the array containing the input data. + * @param inOff offset into the in array the data starts at. + * @param out the array the output data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception IllegalStateException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + throws DataLengthException, IllegalStateException + { + return (encrypting) ? encryptBlock(in, inOff, out, outOff) : decryptBlock(in, inOff, out, outOff); + } + + /** + * reset the chaining vector back to the IV and reset the underlying + * cipher. + */ + public void reset() + { + System.arraycopy(IV, 0, cbcV, 0, IV.length); + Arrays.fill(cbcNextV, (byte)0); + + cipher.reset(); + } + + /** + * Do the appropriate chaining step for CBC mode encryption. + * + * @param in the array containing the data to be encrypted. + * @param inOff offset into the in array the data starts at. + * @param out the array the encrypted data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception IllegalStateException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + private int encryptBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + throws DataLengthException, IllegalStateException + { + if ((inOff + blockSize) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + /* + * XOR the cbcV and the input, + * then encrypt the cbcV + */ + for (int i = 0; i < blockSize; i++) + { + cbcV[i] ^= in[inOff + i]; + } + + int length = cipher.processBlock(cbcV, 0, out, outOff); + + /* + * copy ciphertext to cbcV + */ + System.arraycopy(out, outOff, cbcV, 0, cbcV.length); + + return length; + } + + /** + * Do the appropriate chaining step for CBC mode decryption. + * + * @param in the array containing the data to be decrypted. + * @param inOff offset into the in array the data starts at. + * @param out the array the decrypted data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception IllegalStateException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + private int decryptBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + throws DataLengthException, IllegalStateException + { + if ((inOff + blockSize) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + System.arraycopy(in, inOff, cbcNextV, 0, blockSize); + + int length = cipher.processBlock(in, inOff, out, outOff); + + /* + * XOR the cbcV and the output + */ + for (int i = 0; i < blockSize; i++) + { + out[outOff + i] ^= cbcV[i]; + } + + /* + * swap the back up buffer into next position + */ + byte[] tmp; + + tmp = cbcV; + cbcV = cbcNextV; + cbcNextV = tmp; + + return length; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/modes/CFBBlockCipher.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/modes/CFBBlockCipher.java new file mode 100644 index 00000000..0de04502 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/modes/CFBBlockCipher.java @@ -0,0 +1,250 @@ +package org.bouncycastle.crypto.modes; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.params.ParametersWithIV; + +/** + * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher. + */ +public class CFBBlockCipher + implements BlockCipher +{ + private byte[] IV; + private byte[] cfbV; + private byte[] cfbOutV; + + private int blockSize; + private BlockCipher cipher = null; + private boolean encrypting; + + /** + * Basic constructor. + * + * @param cipher the block cipher to be used as the basis of the + * feedback mode. + * @param bitBlockSize the block size in bits (note: a multiple of 8) + */ + public CFBBlockCipher( + BlockCipher cipher, + int bitBlockSize) + { + this.cipher = cipher; + this.blockSize = bitBlockSize / 8; + + this.IV = new byte[cipher.getBlockSize()]; + this.cfbV = new byte[cipher.getBlockSize()]; + this.cfbOutV = new byte[cipher.getBlockSize()]; + } + + /** + * return the underlying block cipher that we are wrapping. + * + * @return the underlying block cipher that we are wrapping. + */ + public BlockCipher getUnderlyingCipher() + { + return cipher; + } + + /** + * Initialise the cipher and, possibly, the initialisation vector (IV). + * If an IV isn't passed as part of the parameter, the IV will be all zeros. + * An IV which is too short is handled in FIPS compliant fashion. + * + * @param encrypting if true the cipher is initialised for + * encryption, if false for decryption. + * @param params the key and other data required by the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean encrypting, + CipherParameters params) + throws IllegalArgumentException + { + this.encrypting = encrypting; + + if (params instanceof ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV)params; + byte[] iv = ivParam.getIV(); + + if (iv.length < IV.length) + { + // prepend the supplied IV with zeros (per FIPS PUB 81) + System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length); + for (int i = 0; i < IV.length - iv.length; i++) + { + IV[i] = 0; + } + } + else + { + System.arraycopy(iv, 0, IV, 0, IV.length); + } + + reset(); + + cipher.init(true, ivParam.getParameters()); + } + else + { + reset(); + + cipher.init(true, params); + } + } + + /** + * return the algorithm name and mode. + * + * @return the name of the underlying algorithm followed by "/CFB" + * and the block size in bits. + */ + public String getAlgorithmName() + { + return cipher.getAlgorithmName() + "/CFB" + (blockSize * 8); + } + + /** + * return the block size we are operating at. + * + * @return the block size we are operating at (in bytes). + */ + public int getBlockSize() + { + return blockSize; + } + + /** + * Process one block of input from the array in and write it to + * the out array. + * + * @param in the array containing the input data. + * @param inOff offset into the in array the data starts at. + * @param out the array the output data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception IllegalStateException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + throws DataLengthException, IllegalStateException + { + return (encrypting) ? encryptBlock(in, inOff, out, outOff) : decryptBlock(in, inOff, out, outOff); + } + + /** + * Do the appropriate processing for CFB mode encryption. + * + * @param in the array containing the data to be encrypted. + * @param inOff offset into the in array the data starts at. + * @param out the array the encrypted data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception IllegalStateException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int encryptBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + throws DataLengthException, IllegalStateException + { + if ((inOff + blockSize) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + blockSize) > out.length) + { + throw new DataLengthException("output buffer too short"); + } + + cipher.processBlock(cfbV, 0, cfbOutV, 0); + + // + // XOR the cfbV with the plaintext producing the ciphertext + // + for (int i = 0; i < blockSize; i++) + { + out[outOff + i] = (byte)(cfbOutV[i] ^ in[inOff + i]); + } + + // + // change over the input block. + // + System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length - blockSize); + System.arraycopy(out, outOff, cfbV, cfbV.length - blockSize, blockSize); + + return blockSize; + } + + /** + * Do the appropriate processing for CFB mode decryption. + * + * @param in the array containing the data to be decrypted. + * @param inOff offset into the in array the data starts at. + * @param out the array the encrypted data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception IllegalStateException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int decryptBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + throws DataLengthException, IllegalStateException + { + if ((inOff + blockSize) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + blockSize) > out.length) + { + throw new DataLengthException("output buffer too short"); + } + + cipher.processBlock(cfbV, 0, cfbOutV, 0); + + // + // change over the input block. + // + System.arraycopy(cfbV, blockSize, cfbV, 0, cfbV.length - blockSize); + System.arraycopy(in, inOff, cfbV, cfbV.length - blockSize, blockSize); + + // + // XOR the cfbV with the ciphertext producing the plaintext + // + for (int i = 0; i < blockSize; i++) + { + out[outOff + i] = (byte)(cfbOutV[i] ^ in[inOff + i]); + } + + return blockSize; + } + + /** + * reset the chaining vector back to the IV and reset the underlying + * cipher. + */ + public void reset() + { + System.arraycopy(IV, 0, cfbV, 0, IV.length); + + cipher.reset(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/modes/CTSBlockCipher.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/modes/CTSBlockCipher.java new file mode 100644 index 00000000..b8e5b610 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/modes/CTSBlockCipher.java @@ -0,0 +1,265 @@ +package org.bouncycastle.crypto.modes; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.BufferedBlockCipher; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.InvalidCipherTextException; + +/** + * A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to + * be used to produce cipher text which is the same length as the plain text. + */ +public class CTSBlockCipher + extends BufferedBlockCipher +{ + private int blockSize; + + /** + * Create a buffered block cipher that uses Cipher Text Stealing + * + * @param cipher the underlying block cipher this buffering object wraps. + */ + public CTSBlockCipher( + BlockCipher cipher) + { + if ((cipher instanceof OFBBlockCipher) || (cipher instanceof CFBBlockCipher)) + { + throw new IllegalArgumentException("CTSBlockCipher can only accept ECB, or CBC ciphers"); + } + + this.cipher = cipher; + + blockSize = cipher.getBlockSize(); + + buf = new byte[blockSize * 2]; + bufOff = 0; + } + + /** + * return the size of the output buffer required for an update + * an input of len bytes. + * + * @param len the length of the input. + * @return the space required to accommodate a call to update + * with len bytes of input. + */ + public int getUpdateOutputSize( + int len) + { + int total = len + bufOff; + int leftOver = total % buf.length; + + if (leftOver == 0) + { + return total - buf.length; + } + + return total - leftOver; + } + + /** + * return the size of the output buffer required for an update plus a + * doFinal with an input of len bytes. + * + * @param len the length of the input. + * @return the space required to accommodate a call to update and doFinal + * with len bytes of input. + */ + public int getOutputSize( + int len) + { + return len + bufOff; + } + + /** + * process a single byte, producing an output block if neccessary. + * + * @param in the input byte. + * @param out the space for any output that might be produced. + * @param outOff the offset from which the output will be copied. + * @return the number of output bytes copied to out. + * @exception DataLengthException if there isn't enough space in out. + * @exception IllegalStateException if the cipher isn't initialised. + */ + public int processByte( + byte in, + byte[] out, + int outOff) + throws DataLengthException, IllegalStateException + { + int resultLen = 0; + + if (bufOff == buf.length) + { + resultLen = cipher.processBlock(buf, 0, out, outOff); + System.arraycopy(buf, blockSize, buf, 0, blockSize); + + bufOff = blockSize; + } + + buf[bufOff++] = in; + + return resultLen; + } + + /** + * process an array of bytes, producing output if necessary. + * + * @param in the input byte array. + * @param inOff the offset at which the input data starts. + * @param len the number of bytes to be copied out of the input array. + * @param out the space for any output that might be produced. + * @param outOff the offset from which the output will be copied. + * @return the number of output bytes copied to out. + * @exception DataLengthException if there isn't enough space in out. + * @exception IllegalStateException if the cipher isn't initialised. + */ + public int processBytes( + byte[] in, + int inOff, + int len, + byte[] out, + int outOff) + throws DataLengthException, IllegalStateException + { + if (len < 0) + { + throw new IllegalArgumentException("Can't have a negative input length!"); + } + + int blockSize = getBlockSize(); + int length = getUpdateOutputSize(len); + + if (length > 0) + { + if ((outOff + length) > out.length) + { + throw new DataLengthException("output buffer too short"); + } + } + + int resultLen = 0; + int gapLen = buf.length - bufOff; + + if (len > gapLen) + { + System.arraycopy(in, inOff, buf, bufOff, gapLen); + + resultLen += cipher.processBlock(buf, 0, out, outOff); + System.arraycopy(buf, blockSize, buf, 0, blockSize); + + bufOff = blockSize; + + len -= gapLen; + inOff += gapLen; + + while (len > blockSize) + { + System.arraycopy(in, inOff, buf, bufOff, blockSize); + resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen); + System.arraycopy(buf, blockSize, buf, 0, blockSize); + + len -= blockSize; + inOff += blockSize; + } + } + + System.arraycopy(in, inOff, buf, bufOff, len); + + bufOff += len; + + return resultLen; + } + + /** + * Process the last block in the buffer. + * + * @param out the array the block currently being held is copied into. + * @param outOff the offset at which the copying starts. + * @return the number of output bytes copied to out. + * @exception DataLengthException if there is insufficient space in out for + * the output. + * @exception IllegalStateException if the underlying cipher is not + * initialised. + * @exception InvalidCipherTextException if cipher text decrypts wrongly (in + * case the exception will never get thrown). + */ + public int doFinal( + byte[] out, + int outOff) + throws DataLengthException, IllegalStateException, InvalidCipherTextException + { + if (bufOff + outOff > out.length) + { + throw new DataLengthException("output buffer to small in doFinal"); + } + + int blockSize = cipher.getBlockSize(); + int len = bufOff - blockSize; + byte[] block = new byte[blockSize]; + + if (forEncryption) + { + cipher.processBlock(buf, 0, block, 0); + + if (bufOff < blockSize) + { + throw new DataLengthException("need at least one block of input for CTS"); + } + + for (int i = bufOff; i != buf.length; i++) + { + buf[i] = block[i - blockSize]; + } + + for (int i = blockSize; i != bufOff; i++) + { + buf[i] ^= block[i - blockSize]; + } + + if (cipher instanceof CBCBlockCipher) + { + BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher(); + + c.processBlock(buf, blockSize, out, outOff); + } + else + { + cipher.processBlock(buf, blockSize, out, outOff); + } + + System.arraycopy(block, 0, out, outOff + blockSize, len); + } + else + { + byte[] lastBlock = new byte[blockSize]; + + if (cipher instanceof CBCBlockCipher) + { + BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher(); + + c.processBlock(buf, 0, block, 0); + } + else + { + cipher.processBlock(buf, 0, block, 0); + } + + for (int i = blockSize; i != bufOff; i++) + { + lastBlock[i - blockSize] = (byte)(block[i - blockSize] ^ buf[i]); + } + + System.arraycopy(buf, blockSize, block, 0, len); + + cipher.processBlock(block, 0, out, outOff); + System.arraycopy(lastBlock, 0, out, outOff + blockSize, len); + } + + int offset = bufOff; + + reset(); + + return offset; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/modes/OFBBlockCipher.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/modes/OFBBlockCipher.java new file mode 100644 index 00000000..f209b9fc --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/modes/OFBBlockCipher.java @@ -0,0 +1,179 @@ +package org.bouncycastle.crypto.modes; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.params.ParametersWithIV; + +/** + * implements a Output-FeedBack (OFB) mode on top of a simple cipher. + */ +public class OFBBlockCipher + implements BlockCipher +{ + private byte[] IV; + private byte[] ofbV; + private byte[] ofbOutV; + + private final int blockSize; + private final BlockCipher cipher; + + /** + * Basic constructor. + * + * @param cipher the block cipher to be used as the basis of the + * feedback mode. + * @param blockSize the block size in bits (note: a multiple of 8) + */ + public OFBBlockCipher( + BlockCipher cipher, + int blockSize) + { + this.cipher = cipher; + this.blockSize = blockSize / 8; + + this.IV = new byte[cipher.getBlockSize()]; + this.ofbV = new byte[cipher.getBlockSize()]; + this.ofbOutV = new byte[cipher.getBlockSize()]; + } + + /** + * return the underlying block cipher that we are wrapping. + * + * @return the underlying block cipher that we are wrapping. + */ + public BlockCipher getUnderlyingCipher() + { + return cipher; + } + + /** + * Initialise the cipher and, possibly, the initialisation vector (IV). + * If an IV isn't passed as part of the parameter, the IV will be all zeros. + * An IV which is too short is handled in FIPS compliant fashion. + * + * @param encrypting if true the cipher is initialised for + * encryption, if false for decryption. + * @param params the key and other data required by the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean encrypting, //ignored by this OFB mode + CipherParameters params) + throws IllegalArgumentException + { + if (params instanceof ParametersWithIV) + { + ParametersWithIV ivParam = (ParametersWithIV)params; + byte[] iv = ivParam.getIV(); + + if (iv.length < IV.length) + { + // prepend the supplied IV with zeros (per FIPS PUB 81) + System.arraycopy(iv, 0, IV, IV.length - iv.length, iv.length); + for (int i = 0; i < IV.length - iv.length; i++) + { + IV[i] = 0; + } + } + else + { + System.arraycopy(iv, 0, IV, 0, IV.length); + } + + reset(); + + cipher.init(true, ivParam.getParameters()); + } + else + { + reset(); + + cipher.init(true, params); + } + } + + /** + * return the algorithm name and mode. + * + * @return the name of the underlying algorithm followed by "/OFB" + * and the block size in bits + */ + public String getAlgorithmName() + { + return cipher.getAlgorithmName() + "/OFB" + (blockSize * 8); + } + + + /** + * return the block size we are operating at (in bytes). + * + * @return the block size we are operating at (in bytes). + */ + public int getBlockSize() + { + return blockSize; + } + + /** + * Process one block of input from the array in and write it to + * the out array. + * + * @param in the array containing the input data. + * @param inOff offset into the in array the data starts at. + * @param out the array the output data will be copied into. + * @param outOff the offset into the out array the output will start at. + * @exception DataLengthException if there isn't enough data in in, or + * space in out. + * @exception IllegalStateException if the cipher isn't initialised. + * @return the number of bytes processed and produced. + */ + public int processBlock( + byte[] in, + int inOff, + byte[] out, + int outOff) + throws DataLengthException, IllegalStateException + { + if ((inOff + blockSize) > in.length) + { + throw new DataLengthException("input buffer too short"); + } + + if ((outOff + blockSize) > out.length) + { + throw new DataLengthException("output buffer too short"); + } + + cipher.processBlock(ofbV, 0, ofbOutV, 0); + + // + // XOR the ofbV with the plaintext producing the cipher text (and + // the next input block). + // + for (int i = 0; i < blockSize; i++) + { + out[outOff + i] = (byte)(ofbOutV[i] ^ in[inOff + i]); + } + + // + // change over the input block. + // + System.arraycopy(ofbV, blockSize, ofbV, 0, ofbV.length - blockSize); + System.arraycopy(ofbOutV, 0, ofbV, ofbV.length - blockSize, blockSize); + + return blockSize; + } + + /** + * reset the feedback vector back to the IV and reset the underlying + * cipher. + */ + public void reset() + { + System.arraycopy(IV, 0, ofbV, 0, IV.length); + + cipher.reset(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/paddings/BlockCipherPadding.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/paddings/BlockCipherPadding.java new file mode 100644 index 00000000..7c4f0aee --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/paddings/BlockCipherPadding.java @@ -0,0 +1,48 @@ +package org.bouncycastle.crypto.paddings; + +import java.security.SecureRandom; + +import org.bouncycastle.crypto.InvalidCipherTextException; + +/** + * Block cipher padders are expected to conform to this interface + */ +public interface BlockCipherPadding +{ + /** + * Initialise the padder. + * + * @param random the source of randomness for the padding, if required. + */ + public void init(SecureRandom random) + throws IllegalArgumentException; + + /** + * Return the name of the algorithm the cipher implements. + * + * @return the name of the algorithm the cipher implements. + */ + public String getPaddingName(); + + /** + * add the pad bytes to the passed in block, returning the + * number of bytes added. + *

+ * Note: this assumes that the last block of plain text is always + * passed to it inside in. i.e. if inOff is zero, indicating the + * entire block is to be overwritten with padding the value of in + * should be the same as the last block of plain text. The reason + * for this is that some modes such as "trailing bit compliment" + * base the padding on the last byte of plain text. + *

+ */ + public int addPadding(byte[] in, int inOff); + + /** + * return the number of pad bytes present in the block. + * @exception InvalidCipherTextException if the padding is badly formed + * or invalid. + */ + public int padCount(byte[] in) + throws InvalidCipherTextException; +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/paddings/PKCS7Padding.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/paddings/PKCS7Padding.java new file mode 100644 index 00000000..93b149fa --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/paddings/PKCS7Padding.java @@ -0,0 +1,76 @@ +package org.bouncycastle.crypto.paddings; + +import java.security.SecureRandom; + +import org.bouncycastle.crypto.InvalidCipherTextException; + +/** + * A padder that adds PKCS7/PKCS5 padding to a block. + */ +public class PKCS7Padding + implements BlockCipherPadding +{ + /** + * Initialise the padder. + * + * @param random - a SecureRandom if available. + */ + public void init(SecureRandom random) + throws IllegalArgumentException + { + // nothing to do. + } + + /** + * Return the name of the algorithm the padder implements. + * + * @return the name of the algorithm the padder implements. + */ + public String getPaddingName() + { + return "PKCS7"; + } + + /** + * add the pad bytes to the passed in block, returning the + * number of bytes added. + */ + public int addPadding( + byte[] in, + int inOff) + { + byte code = (byte)(in.length - inOff); + + while (inOff < in.length) + { + in[inOff] = code; + inOff++; + } + + return code; + } + + /** + * return the number of pad bytes present in the block. + */ + public int padCount(byte[] in) + throws InvalidCipherTextException + { + int count = in[in.length - 1] & 0xff; + + if (count > in.length || count == 0) + { + throw new InvalidCipherTextException("pad block corrupted"); + } + + for (int i = 1; i <= count; i++) + { + if (in[in.length - i] != count) + { + throw new InvalidCipherTextException("pad block corrupted"); + } + } + + return count; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java new file mode 100644 index 00000000..ec412b95 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/paddings/PaddedBufferedBlockCipher.java @@ -0,0 +1,298 @@ +package org.bouncycastle.crypto.paddings; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.BufferedBlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.params.ParametersWithRandom; + +/** + * A wrapper class that allows block ciphers to be used to process data in + * a piecemeal fashion with padding. The PaddedBufferedBlockCipher + * outputs a block only when the buffer is full and more data is being added, + * or on a doFinal (unless the current block in the buffer is a pad block). + * The default padding mechanism used is the one outlined in PKCS5/PKCS7. + */ +public class PaddedBufferedBlockCipher + extends BufferedBlockCipher +{ + BlockCipherPadding padding; + + /** + * Create a buffered block cipher with the desired padding. + * + * @param cipher the underlying block cipher this buffering object wraps. + * @param padding the padding type. + */ + public PaddedBufferedBlockCipher( + BlockCipher cipher, + BlockCipherPadding padding) + { + this.cipher = cipher; + this.padding = padding; + + buf = new byte[cipher.getBlockSize()]; + bufOff = 0; + } + + /** + * Create a buffered block cipher PKCS7 padding + * + * @param cipher the underlying block cipher this buffering object wraps. + */ + public PaddedBufferedBlockCipher( + BlockCipher cipher) + { + this(cipher, new PKCS7Padding()); + } + + /** + * initialise the cipher. + * + * @param forEncryption if true the cipher is initialised for + * encryption, if false for decryption. + * @param params the key and other data required by the cipher. + * @exception IllegalArgumentException if the params argument is + * inappropriate. + */ + public void init( + boolean forEncryption, + CipherParameters params) + throws IllegalArgumentException + { + this.forEncryption = forEncryption; + + reset(); + + if (params instanceof ParametersWithRandom) + { + ParametersWithRandom p = (ParametersWithRandom)params; + + padding.init(p.getRandom()); + + cipher.init(forEncryption, p.getParameters()); + } + else + { + padding.init(null); + + cipher.init(forEncryption, params); + } + } + + /** + * return the minimum size of the output buffer required for an update + * plus a doFinal with an input of len bytes. + * + * @param len the length of the input. + * @return the space required to accommodate a call to update and doFinal + * with len bytes of input. + */ + public int getOutputSize( + int len) + { + int total = len + bufOff; + int leftOver = total % buf.length; + + if (leftOver == 0) + { + if (forEncryption) + { + return total + buf.length; + } + + return total; + } + + return total - leftOver + buf.length; + } + + /** + * return the size of the output buffer required for an update + * an input of len bytes. + * + * @param len the length of the input. + * @return the space required to accommodate a call to update + * with len bytes of input. + */ + public int getUpdateOutputSize( + int len) + { + int total = len + bufOff; + int leftOver = total % buf.length; + + if (leftOver == 0) + { + return total - buf.length; + } + + return total - leftOver; + } + + /** + * process a single byte, producing an output block if neccessary. + * + * @param in the input byte. + * @param out the space for any output that might be produced. + * @param outOff the offset from which the output will be copied. + * @return the number of output bytes copied to out. + * @exception DataLengthException if there isn't enough space in out. + * @exception IllegalStateException if the cipher isn't initialised. + */ + public int processByte( + byte in, + byte[] out, + int outOff) + throws DataLengthException, IllegalStateException + { + int resultLen = 0; + + if (bufOff == buf.length) + { + resultLen = cipher.processBlock(buf, 0, out, outOff); + bufOff = 0; + } + + buf[bufOff++] = in; + + return resultLen; + } + + /** + * process an array of bytes, producing output if necessary. + * + * @param in the input byte array. + * @param inOff the offset at which the input data starts. + * @param len the number of bytes to be copied out of the input array. + * @param out the space for any output that might be produced. + * @param outOff the offset from which the output will be copied. + * @return the number of output bytes copied to out. + * @exception DataLengthException if there isn't enough space in out. + * @exception IllegalStateException if the cipher isn't initialised. + */ + public int processBytes( + byte[] in, + int inOff, + int len, + byte[] out, + int outOff) + throws DataLengthException, IllegalStateException + { + if (len < 0) + { + throw new IllegalArgumentException("Can't have a negative input length!"); + } + + int blockSize = getBlockSize(); + int length = getUpdateOutputSize(len); + + if (length > 0) + { + if ((outOff + length) > out.length) + { + throw new DataLengthException("output buffer too short"); + } + } + + int resultLen = 0; + int gapLen = buf.length - bufOff; + + if (len > gapLen) + { + System.arraycopy(in, inOff, buf, bufOff, gapLen); + + resultLen += cipher.processBlock(buf, 0, out, outOff); + + bufOff = 0; + len -= gapLen; + inOff += gapLen; + + while (len > buf.length) + { + resultLen += cipher.processBlock(in, inOff, out, outOff + resultLen); + + len -= blockSize; + inOff += blockSize; + } + } + + System.arraycopy(in, inOff, buf, bufOff, len); + + bufOff += len; + + return resultLen; + } + + /** + * Process the last block in the buffer. If the buffer is currently + * full and padding needs to be added a call to doFinal will produce + * 2 * getBlockSize() bytes. + * + * @param out the array the block currently being held is copied into. + * @param outOff the offset at which the copying starts. + * @return the number of output bytes copied to out. + * @exception DataLengthException if there is insufficient space in out for + * the output or we are decrypting and the input is not block size aligned. + * @exception IllegalStateException if the underlying cipher is not + * initialised. + * @exception InvalidCipherTextException if padding is expected and not found. + */ + public int doFinal( + byte[] out, + int outOff) + throws DataLengthException, IllegalStateException, InvalidCipherTextException + { + int blockSize = cipher.getBlockSize(); + int resultLen = 0; + + if (forEncryption) + { + if (bufOff == blockSize) + { + if ((outOff + 2 * blockSize) > out.length) + { + reset(); + + throw new DataLengthException("output buffer too short"); + } + + resultLen = cipher.processBlock(buf, 0, out, outOff); + bufOff = 0; + } + + padding.addPadding(buf, bufOff); + + resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen); + + reset(); + } + else + { + if (bufOff == blockSize) + { + resultLen = cipher.processBlock(buf, 0, buf, 0); + bufOff = 0; + } + else + { + reset(); + + throw new DataLengthException("last block incomplete in decryption"); + } + + try + { + resultLen -= padding.padCount(buf); + + System.arraycopy(buf, 0, out, outOff, resultLen); + } + finally + { + reset(); + } + } + + return resultLen; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/AsymmetricKeyParameter.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/AsymmetricKeyParameter.java new file mode 100644 index 00000000..03ba7253 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/AsymmetricKeyParameter.java @@ -0,0 +1,20 @@ +package org.bouncycastle.crypto.params; + +import org.bouncycastle.crypto.CipherParameters; + +public class AsymmetricKeyParameter + implements CipherParameters +{ + boolean privateKey; + + public AsymmetricKeyParameter( + boolean privateKey) + { + this.privateKey = privateKey; + } + + public boolean isPrivate() + { + return privateKey; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DESParameters.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DESParameters.java new file mode 100644 index 00000000..5bee3604 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DESParameters.java @@ -0,0 +1,107 @@ +package org.bouncycastle.crypto.params; + +public class DESParameters + extends KeyParameter +{ + public DESParameters( + byte[] key) + { + super(key); + + if (isWeakKey(key, 0)) + { + throw new IllegalArgumentException("attempt to create weak DES key"); + } + } + + /* + * DES Key length in bytes. + */ + static public final int DES_KEY_LENGTH = 8; + + /* + * Table of weak and semi-weak keys taken from Schneier pp281 + */ + static private final int N_DES_WEAK_KEYS = 16; + + static private byte[] DES_weak_keys = + { + /* weak keys */ + (byte)0x01,(byte)0x01,(byte)0x01,(byte)0x01, (byte)0x01,(byte)0x01,(byte)0x01,(byte)0x01, + (byte)0x1f,(byte)0x1f,(byte)0x1f,(byte)0x1f, (byte)0x0e,(byte)0x0e,(byte)0x0e,(byte)0x0e, + (byte)0xe0,(byte)0xe0,(byte)0xe0,(byte)0xe0, (byte)0xf1,(byte)0xf1,(byte)0xf1,(byte)0xf1, + (byte)0xfe,(byte)0xfe,(byte)0xfe,(byte)0xfe, (byte)0xfe,(byte)0xfe,(byte)0xfe,(byte)0xfe, + + /* semi-weak keys */ + (byte)0x01,(byte)0xfe,(byte)0x01,(byte)0xfe, (byte)0x01,(byte)0xfe,(byte)0x01,(byte)0xfe, + (byte)0x1f,(byte)0xe0,(byte)0x1f,(byte)0xe0, (byte)0x0e,(byte)0xf1,(byte)0x0e,(byte)0xf1, + (byte)0x01,(byte)0xe0,(byte)0x01,(byte)0xe0, (byte)0x01,(byte)0xf1,(byte)0x01,(byte)0xf1, + (byte)0x1f,(byte)0xfe,(byte)0x1f,(byte)0xfe, (byte)0x0e,(byte)0xfe,(byte)0x0e,(byte)0xfe, + (byte)0x01,(byte)0x1f,(byte)0x01,(byte)0x1f, (byte)0x01,(byte)0x0e,(byte)0x01,(byte)0x0e, + (byte)0xe0,(byte)0xfe,(byte)0xe0,(byte)0xfe, (byte)0xf1,(byte)0xfe,(byte)0xf1,(byte)0xfe, + (byte)0xfe,(byte)0x01,(byte)0xfe,(byte)0x01, (byte)0xfe,(byte)0x01,(byte)0xfe,(byte)0x01, + (byte)0xe0,(byte)0x1f,(byte)0xe0,(byte)0x1f, (byte)0xf1,(byte)0x0e,(byte)0xf1,(byte)0x0e, + (byte)0xe0,(byte)0x01,(byte)0xe0,(byte)0x01, (byte)0xf1,(byte)0x01,(byte)0xf1,(byte)0x01, + (byte)0xfe,(byte)0x1f,(byte)0xfe,(byte)0x1f, (byte)0xfe,(byte)0x0e,(byte)0xfe,(byte)0x0e, + (byte)0x1f,(byte)0x01,(byte)0x1f,(byte)0x01, (byte)0x0e,(byte)0x01,(byte)0x0e,(byte)0x01, + (byte)0xfe,(byte)0xe0,(byte)0xfe,(byte)0xe0, (byte)0xfe,(byte)0xf1,(byte)0xfe,(byte)0xf1 + }; + + /** + * DES has 16 weak keys. This method will check + * if the given DES key material is weak or semi-weak. + * Key material that is too short is regarded as weak. + *

+ * See "Applied + * Cryptography" by Bruce Schneier for more information. + * + * @return true if the given DES key material is weak or semi-weak, + * false otherwise. + */ + public static boolean isWeakKey( + byte[] key, + int offset) + { + if (key.length - offset < DES_KEY_LENGTH) + { + throw new IllegalArgumentException("key material too short."); + } + + nextkey: for (int i = 0; i < N_DES_WEAK_KEYS; i++) + { + for (int j = 0; j < DES_KEY_LENGTH; j++) + { + if (key[j + offset] != DES_weak_keys[i * DES_KEY_LENGTH + j]) + { + continue nextkey; + } + } + + return true; + } + return false; + } + + /** + * DES Keys use the LSB as the odd parity bit. This can + * be used to check for corrupt keys. + * + * @param bytes the byte array to set the parity on. + */ + public static void setOddParity( + byte[] bytes) + { + for (int i = 0; i < bytes.length; i++) + { + int b = bytes[i]; + bytes[i] = (byte)((b & 0xfe) | + ((((b >> 1) ^ + (b >> 2) ^ + (b >> 3) ^ + (b >> 4) ^ + (b >> 5) ^ + (b >> 6) ^ + (b >> 7)) ^ 0x01) & 0x01)); + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DHKeyParameters.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DHKeyParameters.java new file mode 100644 index 00000000..e686f357 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DHKeyParameters.java @@ -0,0 +1,54 @@ +package org.bouncycastle.crypto.params; + + +public class DHKeyParameters + extends AsymmetricKeyParameter +{ + private DHParameters params; + + protected DHKeyParameters( + boolean isPrivate, + DHParameters params) + { + super(isPrivate); + + this.params = params; + } + + public DHParameters getParameters() + { + return params; + } + + public boolean equals( + Object obj) + { + if (!(obj instanceof DHKeyParameters)) + { + return false; + } + + DHKeyParameters dhKey = (DHKeyParameters)obj; + + if (params == null) + { + return dhKey.getParameters() == null; + } + else + { + return params.equals(dhKey.getParameters()); + } + } + + public int hashCode() + { + int code = isPrivate() ? 0 : 1; + + if (params != null) + { + code ^= params.hashCode(); + } + + return code; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DHParameters.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DHParameters.java new file mode 100644 index 00000000..95352cee --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DHParameters.java @@ -0,0 +1,188 @@ +package org.bouncycastle.crypto.params; + +import org.bouncycastle.crypto.CipherParameters; + +import java.math.BigInteger; + +public class DHParameters + implements CipherParameters +{ + private static final int DEFAULT_MINIMUM_LENGTH = 160; + + // not final due to compiler bug in "simpler" JDKs + private BigInteger g; + private BigInteger p; + private BigInteger q; + private BigInteger j; + private int m; + private int l; + private DHValidationParameters validation; + + private static int getDefaultMParam( + int lParam) + { + if (lParam == 0) + { + return DEFAULT_MINIMUM_LENGTH; + } + + return lParam < DEFAULT_MINIMUM_LENGTH ? lParam : DEFAULT_MINIMUM_LENGTH; + } + + public DHParameters( + BigInteger p, + BigInteger g) + { + this(p, g, null, 0); + } + + public DHParameters( + BigInteger p, + BigInteger g, + BigInteger q) + { + this(p, g, q, 0); + } + + public DHParameters( + BigInteger p, + BigInteger g, + BigInteger q, + int l) + { + this(p, g, q, getDefaultMParam(l), l, null, null); + } + + public DHParameters( + BigInteger p, + BigInteger g, + BigInteger q, + int m, + int l) + { + this(p, g, q, m, l, null, null); + } + + public DHParameters( + BigInteger p, + BigInteger g, + BigInteger q, + BigInteger j, + DHValidationParameters validation) + { + this(p, g, q, DEFAULT_MINIMUM_LENGTH, 0, j, validation); + } + + public DHParameters( + BigInteger p, + BigInteger g, + BigInteger q, + int m, + int l, + BigInteger j, + DHValidationParameters validation) + { + if (l != 0) + { + if (l >= p.bitLength()) + { + throw new IllegalArgumentException("when l value specified, it must be less than bitlength(p)"); + } + if (l < m) + { + throw new IllegalArgumentException("when l value specified, it may not be less than m value"); + } + } + + this.g = g; + this.p = p; + this.q = q; + this.m = m; + this.l = l; + this.j = j; + this.validation = validation; + } + + public BigInteger getP() + { + return p; + } + + public BigInteger getG() + { + return g; + } + + public BigInteger getQ() + { + return q; + } + + /** + * Return the subgroup factor J. + * + * @return subgroup factor + */ + public BigInteger getJ() + { + return j; + } + + /** + * Return the minimum length of the private value. + * + * @return the minimum length of the private value in bits. + */ + public int getM() + { + return m; + } + + /** + * Return the private value length in bits - if set, zero otherwise + * + * @return the private value length in bits, zero otherwise. + */ + public int getL() + { + return l; + } + + public DHValidationParameters getValidationParameters() + { + return validation; + } + + public boolean equals( + Object obj) + { + if (!(obj instanceof DHParameters)) + { + return false; + } + + DHParameters pm = (DHParameters)obj; + + if (this.getQ() != null) + { + if (!this.getQ().equals(pm.getQ())) + { + return false; + } + } + else + { + if (pm.getQ() != null) + { + return false; + } + } + + return pm.getP().equals(p) && pm.getG().equals(g); + } + + public int hashCode() + { + return getP().hashCode() ^ getG().hashCode() ^ (getQ() != null ? getQ().hashCode() : 0); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DHPrivateKeyParameters.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DHPrivateKeyParameters.java new file mode 100644 index 00000000..ee1b34f9 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DHPrivateKeyParameters.java @@ -0,0 +1,41 @@ +package org.bouncycastle.crypto.params; + +import java.math.BigInteger; + +public class DHPrivateKeyParameters + extends DHKeyParameters +{ + private BigInteger x; + + public DHPrivateKeyParameters( + BigInteger x, + DHParameters params) + { + super(true, params); + + this.x = x; + } + + public BigInteger getX() + { + return x; + } + + public int hashCode() + { + return x.hashCode() ^ super.hashCode(); + } + + public boolean equals( + Object obj) + { + if (!(obj instanceof DHPrivateKeyParameters)) + { + return false; + } + + DHPrivateKeyParameters other = (DHPrivateKeyParameters)obj; + + return other.getX().equals(this.x) && super.equals(obj); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DHValidationParameters.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DHValidationParameters.java new file mode 100644 index 00000000..b22f7a03 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DHValidationParameters.java @@ -0,0 +1,50 @@ +package org.bouncycastle.crypto.params; + +import org.bouncycastle.util.Arrays; + +public class DHValidationParameters +{ + private byte[] seed; + private int counter; + + public DHValidationParameters( + byte[] seed, + int counter) + { + this.seed = seed; + this.counter = counter; + } + + public int getCounter() + { + return counter; + } + + public byte[] getSeed() + { + return seed; + } + + public boolean equals( + Object o) + { + if (!(o instanceof DHValidationParameters)) + { + return false; + } + + DHValidationParameters other = (DHValidationParameters)o; + + if (other.counter != this.counter) + { + return false; + } + + return Arrays.areEqual(this.seed, other.seed); + } + + public int hashCode() + { + return counter ^ Arrays.hashCode(seed); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DSAKeyParameters.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DSAKeyParameters.java new file mode 100644 index 00000000..11bb9d97 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DSAKeyParameters.java @@ -0,0 +1,21 @@ +package org.bouncycastle.crypto.params; + +public class DSAKeyParameters + extends AsymmetricKeyParameter +{ + private DSAParameters params; + + public DSAKeyParameters( + boolean isPrivate, + DSAParameters params) + { + super(isPrivate); + + this.params = params; + } + + public DSAParameters getParameters() + { + return params; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DSAParameters.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DSAParameters.java new file mode 100644 index 00000000..7f76d117 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DSAParameters.java @@ -0,0 +1,74 @@ +package org.bouncycastle.crypto.params; + +import java.math.BigInteger; + +import org.bouncycastle.crypto.CipherParameters; + +public class DSAParameters + implements CipherParameters +{ + private BigInteger g; + private BigInteger q; + private BigInteger p; + private DSAValidationParameters validation; + + public DSAParameters( + BigInteger p, + BigInteger q, + BigInteger g) + { + this.g = g; + this.p = p; + this.q = q; + } + + public DSAParameters( + BigInteger p, + BigInteger q, + BigInteger g, + DSAValidationParameters params) + { + this.g = g; + this.p = p; + this.q = q; + this.validation = params; + } + + public BigInteger getP() + { + return p; + } + + public BigInteger getQ() + { + return q; + } + + public BigInteger getG() + { + return g; + } + + public DSAValidationParameters getValidationParameters() + { + return validation; + } + + public boolean equals( + Object obj) + { + if (!(obj instanceof DSAParameters)) + { + return false; + } + + DSAParameters pm = (DSAParameters)obj; + + return (pm.getP().equals(p) && pm.getQ().equals(q) && pm.getG().equals(g)); + } + + public int hashCode() + { + return getP().hashCode() ^ getQ().hashCode() ^ getG().hashCode(); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DSAPrivateKeyParameters.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DSAPrivateKeyParameters.java new file mode 100644 index 00000000..3bef3f40 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DSAPrivateKeyParameters.java @@ -0,0 +1,23 @@ +package org.bouncycastle.crypto.params; + +import java.math.BigInteger; + +public class DSAPrivateKeyParameters + extends DSAKeyParameters +{ + private BigInteger x; + + public DSAPrivateKeyParameters( + BigInteger x, + DSAParameters params) + { + super(true, params); + + this.x = x; + } + + public BigInteger getX() + { + return x; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DSAValidationParameters.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DSAValidationParameters.java new file mode 100644 index 00000000..1cc4b93e --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/DSAValidationParameters.java @@ -0,0 +1,50 @@ +package org.bouncycastle.crypto.params; + +import org.bouncycastle.util.Arrays; + +public class DSAValidationParameters +{ + private byte[] seed; + private int counter; + + public DSAValidationParameters( + byte[] seed, + int counter) + { + this.seed = seed; + this.counter = counter; + } + + public int getCounter() + { + return counter; + } + + public byte[] getSeed() + { + return seed; + } + + public int hashCode() + { + return counter ^ Arrays.hashCode(seed); + } + + public boolean equals( + Object o) + { + if (!(o instanceof DSAValidationParameters)) + { + return false; + } + + DSAValidationParameters other = (DSAValidationParameters)o; + + if (other.counter != this.counter) + { + return false; + } + + return Arrays.areEqual(this.seed, other.seed); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/KeyParameter.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/KeyParameter.java new file mode 100644 index 00000000..5c4fe0e0 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/KeyParameter.java @@ -0,0 +1,30 @@ +package org.bouncycastle.crypto.params; + +import org.bouncycastle.crypto.CipherParameters; + +public class KeyParameter + implements CipherParameters +{ + private byte[] key; + + public KeyParameter( + byte[] key) + { + this(key, 0, key.length); + } + + public KeyParameter( + byte[] key, + int keyOff, + int keyLen) + { + this.key = new byte[keyLen]; + + System.arraycopy(key, keyOff, this.key, 0, keyLen); + } + + public byte[] getKey() + { + return key; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/ParametersWithIV.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/ParametersWithIV.java new file mode 100644 index 00000000..4a1e6e9a --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/ParametersWithIV.java @@ -0,0 +1,39 @@ +package org.bouncycastle.crypto.params; + +import org.bouncycastle.crypto.CipherParameters; + +public class ParametersWithIV + implements CipherParameters +{ + private byte[] iv; + private CipherParameters parameters; + + public ParametersWithIV( + CipherParameters parameters, + byte[] iv) + { + this(parameters, iv, 0, iv.length); + } + + public ParametersWithIV( + CipherParameters parameters, + byte[] iv, + int ivOff, + int ivLen) + { + this.iv = new byte[ivLen]; + this.parameters = parameters; + + System.arraycopy(iv, ivOff, this.iv, 0, ivLen); + } + + public byte[] getIV() + { + return iv; + } + + public CipherParameters getParameters() + { + return parameters; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/ParametersWithRandom.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/ParametersWithRandom.java new file mode 100644 index 00000000..a7b18e51 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/ParametersWithRandom.java @@ -0,0 +1,36 @@ +package org.bouncycastle.crypto.params; + +import org.bouncycastle.crypto.CipherParameters; + +import java.security.SecureRandom; + +public class ParametersWithRandom + implements CipherParameters +{ + private SecureRandom random; + private CipherParameters parameters; + + public ParametersWithRandom( + CipherParameters parameters, + SecureRandom random) + { + this.random = random; + this.parameters = parameters; + } + + public ParametersWithRandom( + CipherParameters parameters) + { + this(parameters, new SecureRandom()); + } + + public SecureRandom getRandom() + { + return random; + } + + public CipherParameters getParameters() + { + return parameters; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/RSAKeyParameters.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/RSAKeyParameters.java new file mode 100644 index 00000000..4a2d9354 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/RSAKeyParameters.java @@ -0,0 +1,31 @@ +package org.bouncycastle.crypto.params; + +import java.math.BigInteger; + +public class RSAKeyParameters + extends AsymmetricKeyParameter +{ + private BigInteger modulus; + private BigInteger exponent; + + public RSAKeyParameters( + boolean isPrivate, + BigInteger modulus, + BigInteger exponent) + { + super(isPrivate); + + this.modulus = modulus; + this.exponent = exponent; + } + + public BigInteger getModulus() + { + return modulus; + } + + public BigInteger getExponent() + { + return exponent; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java new file mode 100644 index 00000000..b61cb5c4 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/params/RSAPrivateCrtKeyParameters.java @@ -0,0 +1,67 @@ +package org.bouncycastle.crypto.params; + +import java.math.BigInteger; + +public class RSAPrivateCrtKeyParameters + extends RSAKeyParameters +{ + private BigInteger e; + private BigInteger p; + private BigInteger q; + private BigInteger dP; + private BigInteger dQ; + private BigInteger qInv; + + /** + * + */ + public RSAPrivateCrtKeyParameters( + BigInteger modulus, + BigInteger publicExponent, + BigInteger privateExponent, + BigInteger p, + BigInteger q, + BigInteger dP, + BigInteger dQ, + BigInteger qInv) + { + super(true, modulus, privateExponent); + + this.e = publicExponent; + this.p = p; + this.q = q; + this.dP = dP; + this.dQ = dQ; + this.qInv = qInv; + } + + public BigInteger getPublicExponent() + { + return e; + } + + public BigInteger getP() + { + return p; + } + + public BigInteger getQ() + { + return q; + } + + public BigInteger getDP() + { + return dP; + } + + public BigInteger getDQ() + { + return dQ; + } + + public BigInteger getQInv() + { + return qInv; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/util/Pack.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/util/Pack.java new file mode 100644 index 00000000..12b59998 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/crypto/util/Pack.java @@ -0,0 +1,34 @@ +package org.bouncycastle.crypto.util; + +public abstract class Pack +{ + public static int bigEndianToInt(byte[] bs, int off) + { + int n = bs[ off] << 24; + n |= (bs[++off] & 0xff) << 16; + n |= (bs[++off] & 0xff) << 8; + n |= (bs[++off] & 0xff); + return n; + } + + public static void intToBigEndian(int n, byte[] bs, int off) + { + bs[ off] = (byte)(n >>> 24); + bs[++off] = (byte)(n >>> 16); + bs[++off] = (byte)(n >>> 8); + bs[++off] = (byte)(n ); + } + + public static long bigEndianToLong(byte[] bs, int off) + { + int hi = bigEndianToInt(bs, off); + int lo = bigEndianToInt(bs, off + 4); + return ((long)(hi & 0xffffffffL) << 32) | (long)(lo & 0xffffffffL); + } + + public static void longToBigEndian(long n, byte[] bs, int off) + { + intToBigEndian((int)(n >>> 32), bs, off); + intToBigEndian((int)(n & 0xffffffffL), bs, off + 4); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/interfaces/ConfigurableProvider.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/interfaces/ConfigurableProvider.java new file mode 100644 index 00000000..5aa2d9c6 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/interfaces/ConfigurableProvider.java @@ -0,0 +1,13 @@ +package org.bouncycastle.jce.interfaces; + +/** + * Implemented by the BC provider. This allows setting of hidden parameters, + * such as the ImplicitCA parameters from X.962, if used. + */ +public interface ConfigurableProvider +{ + static final String THREAD_LOCAL_EC_IMPLICITLY_CA = "threadLocalEcImplicitlyCa"; + static final String EC_IMPLICITLY_CA = "ecImplicitlyCa"; + + void setParameter(String parameterName, Object parameter); +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/interfaces/PKCS12BagAttributeCarrier.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/interfaces/PKCS12BagAttributeCarrier.java new file mode 100644 index 00000000..25f2947e --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/interfaces/PKCS12BagAttributeCarrier.java @@ -0,0 +1,22 @@ +package org.bouncycastle.jce.interfaces; + +import java.util.Enumeration; + +import org.bouncycastle.asn1.DEREncodable; +import org.bouncycastle.asn1.DERObjectIdentifier; + +/** + * allow us to set attributes on objects that can go into a PKCS12 store. + */ +@SuppressWarnings("unchecked") +public interface PKCS12BagAttributeCarrier +{ + void setBagAttribute( + DERObjectIdentifier oid, + DEREncodable attribute); + + DEREncodable getBagAttribute( + DERObjectIdentifier oid); + + Enumeration getBagAttributeKeys(); +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/provider/JCEBlockCipher.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/provider/JCEBlockCipher.java new file mode 100644 index 00000000..73448fd1 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/provider/JCEBlockCipher.java @@ -0,0 +1,1405 @@ +package org.bouncycastle.jce.provider; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.BufferedBlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.InvalidCipherTextException; +/* +import org.bouncycastle.crypto.engines.AESFastEngine; +import org.bouncycastle.crypto.engines.BlowfishEngine; +import org.bouncycastle.crypto.engines.CAST5Engine; +import org.bouncycastle.crypto.engines.CAST6Engine; +import org.bouncycastle.crypto.engines.DESEngine; +import org.bouncycastle.crypto.engines.DESedeEngine; +import org.bouncycastle.crypto.engines.GOST28147Engine; +import org.bouncycastle.crypto.engines.RC2Engine; +import org.bouncycastle.crypto.engines.RC532Engine; +import org.bouncycastle.crypto.engines.RC564Engine; +import org.bouncycastle.crypto.engines.RC6Engine; +import org.bouncycastle.crypto.engines.RijndaelEngine; +import org.bouncycastle.crypto.engines.SEEDEngine; +import org.bouncycastle.crypto.engines.SerpentEngine; +import org.bouncycastle.crypto.engines.SkipjackEngine; +import org.bouncycastle.crypto.engines.TEAEngine; +*/ +import org.bouncycastle.crypto.engines.TwofishEngine; +//import org.bouncycastle.crypto.engines.XTEAEngine; +//import org.bouncycastle.crypto.modes.AEADBlockCipher; +import org.bouncycastle.crypto.modes.CBCBlockCipher; +//import org.bouncycastle.crypto.modes.CCMBlockCipher; +import org.bouncycastle.crypto.modes.CFBBlockCipher; +import org.bouncycastle.crypto.modes.CTSBlockCipher; +/* +import org.bouncycastle.crypto.modes.EAXBlockCipher; +import org.bouncycastle.crypto.modes.GCMBlockCipher; +import org.bouncycastle.crypto.modes.GOFBBlockCipher; +*/ +import org.bouncycastle.crypto.modes.OFBBlockCipher; +/* +import org.bouncycastle.crypto.modes.OpenPGPCFBBlockCipher; +import org.bouncycastle.crypto.modes.PGPCFBBlockCipher; +import org.bouncycastle.crypto.modes.SICBlockCipher; +import org.bouncycastle.crypto.paddings.BlockCipherPadding; +import org.bouncycastle.crypto.paddings.ISO10126d2Padding; +import org.bouncycastle.crypto.paddings.ISO7816d4Padding; +*/ +import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher; +/* +import org.bouncycastle.crypto.paddings.TBCPadding; +import org.bouncycastle.crypto.paddings.X923Padding; +import org.bouncycastle.crypto.paddings.ZeroBytePadding; +*/ +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; +import org.bouncycastle.crypto.params.ParametersWithRandom; +/* +import org.bouncycastle.crypto.params.ParametersWithSBox; +import org.bouncycastle.crypto.params.RC2Parameters; +import org.bouncycastle.crypto.params.RC5Parameters; +import org.bouncycastle.jce.spec.GOST28147ParameterSpec; +*/ +import org.bouncycastle.util.Strings; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEParameterSpec; +/* +import javax.crypto.spec.RC2ParameterSpec; +import javax.crypto.spec.RC5ParameterSpec; +*/ +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.InvalidParameterException; +import java.security.Key; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +@SuppressWarnings("unchecked") +public class JCEBlockCipher extends WrapCipherSpi + implements PBE +{ + // + // specs we can handle. + // + private Class[] availableSpecs = + { + //RC2ParameterSpec.class, + //RC5ParameterSpec.class, + IvParameterSpec.class, + PBEParameterSpec.class, + //GOST28147ParameterSpec.class + }; + + private BlockCipher baseEngine; + private GenericBlockCipher cipher; + private ParametersWithIV ivParam; + + private int ivLength = 0; + + private boolean padded; + + private PBEParameterSpec pbeSpec = null; + private String pbeAlgorithm = null; + + private String modeName = null; + + protected JCEBlockCipher( + BlockCipher engine) + { + baseEngine = engine; + + cipher = new BufferedGenericBlockCipher(engine); + } + + protected JCEBlockCipher( + BlockCipher engine, + int ivLength) + { + baseEngine = engine; + + this.cipher = new BufferedGenericBlockCipher(engine); + this.ivLength = ivLength / 8; + } + + protected JCEBlockCipher( + BufferedBlockCipher engine, + int ivLength) + { + baseEngine = engine.getUnderlyingCipher(); + + this.cipher = new BufferedGenericBlockCipher(engine); + this.ivLength = ivLength / 8; + } + + protected int engineGetBlockSize() + { + return baseEngine.getBlockSize(); + } + + protected byte[] engineGetIV() + { + return (ivParam != null) ? ivParam.getIV() : null; + } + + protected int engineGetKeySize( + Key key) + { + return key.getEncoded().length * 8; + } + + protected int engineGetOutputSize( + int inputLen) + { + return cipher.getOutputSize(inputLen); + } + + protected AlgorithmParameters engineGetParameters() + { + if (engineParams == null) + { + if (pbeSpec != null) + { + try + { + engineParams = AlgorithmParameters.getInstance(pbeAlgorithm, "BC"); + engineParams.init(pbeSpec); + } + catch (Exception e) + { + return null; + } + } + else if (ivParam != null) + { + String name = cipher.getUnderlyingCipher().getAlgorithmName(); + + if (name.indexOf('/') >= 0) + { + name = name.substring(0, name.indexOf('/')); + } + + try + { + engineParams = AlgorithmParameters.getInstance(name, "BC"); + engineParams.init(ivParam.getIV()); + } + catch (Exception e) + { + throw new RuntimeException(e.toString()); + } + } + } + + return engineParams; + } + + protected void engineSetMode( + String mode) + throws NoSuchAlgorithmException + { + modeName = Strings.toUpperCase(mode); + + if (modeName.equals("ECB")) + { + ivLength = 0; + cipher = new BufferedGenericBlockCipher(baseEngine); + } + else if (modeName.equals("CBC")) + { + ivLength = baseEngine.getBlockSize(); + cipher = new BufferedGenericBlockCipher( + new CBCBlockCipher(baseEngine)); + } + else if (modeName.startsWith("OFB")) + { + ivLength = baseEngine.getBlockSize(); + if (modeName.length() != 3) + { + int wordSize = Integer.parseInt(modeName.substring(3)); + + cipher = new BufferedGenericBlockCipher( + new OFBBlockCipher(baseEngine, wordSize)); + } + else + { + cipher = new BufferedGenericBlockCipher( + new OFBBlockCipher(baseEngine, 8 * baseEngine.getBlockSize())); + } + } + else if (modeName.startsWith("CFB")) + { + ivLength = baseEngine.getBlockSize(); + if (modeName.length() != 3) + { + int wordSize = Integer.parseInt(modeName.substring(3)); + + cipher = new BufferedGenericBlockCipher( + new CFBBlockCipher(baseEngine, wordSize)); + } + else + { + cipher = new BufferedGenericBlockCipher( + new CFBBlockCipher(baseEngine, 8 * baseEngine.getBlockSize())); + } + } + /* + else if (modeName.startsWith("PGP")) + { + boolean inlineIV = modeName.equalsIgnoreCase("PGPCFBwithIV"); + + ivLength = baseEngine.getBlockSize(); + cipher = new BufferedGenericBlockCipher( + new PGPCFBBlockCipher(baseEngine, inlineIV)); + } + else if (modeName.equalsIgnoreCase("OpenPGPCFB")) + { + ivLength = 0; + cipher = new BufferedGenericBlockCipher( + new OpenPGPCFBBlockCipher(baseEngine)); + } + else if (modeName.startsWith("SIC")) + { + ivLength = baseEngine.getBlockSize(); + if (ivLength < 16) + { + throw new IllegalArgumentException("Warning: SIC-Mode can become a twotime-pad if the blocksize of the cipher is too small. Use a cipher with a block size of at least 128 bits (e.g. AES)"); + } + cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher( + new SICBlockCipher(baseEngine))); + } + else if (modeName.startsWith("CTR")) + { + ivLength = baseEngine.getBlockSize(); + cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher( + new SICBlockCipher(baseEngine))); + } + else if (modeName.startsWith("GOFB")) + { + ivLength = baseEngine.getBlockSize(); + cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher( + new GOFBBlockCipher(baseEngine))); + } + else if (modeName.startsWith("CTS")) + { + ivLength = baseEngine.getBlockSize(); + cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(new CBCBlockCipher(baseEngine))); + } + else if (modeName.startsWith("CCM")) + { + ivLength = baseEngine.getBlockSize(); + cipher = new AEADGenericBlockCipher(new CCMBlockCipher(baseEngine)); + } + else if (modeName.startsWith("EAX")) + { + ivLength = baseEngine.getBlockSize(); + cipher = new AEADGenericBlockCipher(new EAXBlockCipher(baseEngine)); + } + else if (modeName.startsWith("GCM")) + { + ivLength = baseEngine.getBlockSize(); + cipher = new AEADGenericBlockCipher(new GCMBlockCipher(baseEngine)); + } + */ + else + { + throw new NoSuchAlgorithmException("can't support mode " + mode); + } + } + + protected void engineSetPadding( + String padding) + throws NoSuchPaddingException + { + String paddingName = Strings.toUpperCase(padding); + + if (paddingName.equals("NOPADDING")) + { + if (cipher.wrapOnNoPadding()) + { + cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(cipher.getUnderlyingCipher())); + } + } + else if (paddingName.equals("WITHCTS")) + { + cipher = new BufferedGenericBlockCipher(new CTSBlockCipher(cipher.getUnderlyingCipher())); + } + else + { + padded = true; + + if (isAEADModeName(modeName)) + { + throw new NoSuchPaddingException("Only NoPadding can be used with AEAD modes."); + } + else if (paddingName.equals("PKCS5PADDING") || paddingName.equals("PKCS7PADDING")) + { + cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher()); + } + /* + else if (paddingName.equals("ZEROBYTEPADDING")) + { + cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new ZeroBytePadding()); + } + else if (paddingName.equals("ISO10126PADDING") || paddingName.equals("ISO10126-2PADDING")) + { + cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new ISO10126d2Padding()); + } + else if (paddingName.equals("X9.23PADDING") || paddingName.equals("X923PADDING")) + { + cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new X923Padding()); + } + else if (paddingName.equals("ISO7816-4PADDING") || paddingName.equals("ISO9797-1PADDING")) + { + cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new ISO7816d4Padding()); + } + else if (paddingName.equals("TBCPADDING")) + { + cipher = new BufferedGenericBlockCipher(cipher.getUnderlyingCipher(), new TBCPadding()); + } + */ + else + { + throw new NoSuchPaddingException("Padding " + padding + " unknown."); + } + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + CipherParameters param; + + this.pbeSpec = null; + this.pbeAlgorithm = null; + this.engineParams = null; + + // + // basic key check + // + if (!(key instanceof SecretKey)) + { + throw new InvalidKeyException("Key for algorithm " + key.getAlgorithm() + " not suitable for symmetric enryption."); + } + + // + // for RC5-64 we must have some default parameters + // + if (params == null && baseEngine.getAlgorithmName().startsWith("RC5-64")) + { + throw new InvalidAlgorithmParameterException("RC5 requires an RC5ParametersSpec to be passed in."); + } + + // + // a note on iv's - if ivLength is zero the IV gets ignored (we don't use it). + // + if (key instanceof JCEPBEKey) + { + JCEPBEKey k = (JCEPBEKey)key; + + if (k.getOID() != null) + { + pbeAlgorithm = k.getOID().getId(); + } + else + { + pbeAlgorithm = k.getAlgorithm(); + } + + if (k.getParam() != null) + { + param = k.getParam(); + pbeSpec = new PBEParameterSpec(k.getSalt(), k.getIterationCount()); + } + else if (params instanceof PBEParameterSpec) + { + pbeSpec = (PBEParameterSpec)params; + param = PBE.Util.makePBEParameters(k, params, cipher.getUnderlyingCipher().getAlgorithmName()); + } + else + { + throw new InvalidAlgorithmParameterException("PBE requires PBE parameters to be set."); + } + + if (param instanceof ParametersWithIV) + { + ivParam = (ParametersWithIV)param; + } + } + else if (params == null) + { + param = new KeyParameter(key.getEncoded()); + } + else if (params instanceof IvParameterSpec) + { + if (ivLength != 0) + { + IvParameterSpec p = (IvParameterSpec)params; + + if (p.getIV().length != ivLength && !isAEADModeName(modeName)) + { + throw new InvalidAlgorithmParameterException("IV must be " + ivLength + " bytes long."); + } + + param = new ParametersWithIV(new KeyParameter(key.getEncoded()), p.getIV()); + ivParam = (ParametersWithIV)param; + } + else + { + if (modeName != null && modeName.equals("ECB")) + { + throw new InvalidAlgorithmParameterException("ECB mode does not use an IV"); + } + + param = new KeyParameter(key.getEncoded()); + } + } + /* + else if (params instanceof GOST28147ParameterSpec) + { + GOST28147ParameterSpec gost28147Param = (GOST28147ParameterSpec)params; + + param = new ParametersWithSBox( + new KeyParameter(key.getEncoded()), ((GOST28147ParameterSpec)params).getSbox()); + + if (gost28147Param.getIV() != null && ivLength != 0) + { + param = new ParametersWithIV(param, gost28147Param.getIV()); + ivParam = (ParametersWithIV)param; + } + } + else if (params instanceof RC2ParameterSpec) + { + RC2ParameterSpec rc2Param = (RC2ParameterSpec)params; + + param = new RC2Parameters(key.getEncoded(), ((RC2ParameterSpec)params).getEffectiveKeyBits()); + + if (rc2Param.getIV() != null && ivLength != 0) + { + param = new ParametersWithIV(param, rc2Param.getIV()); + ivParam = (ParametersWithIV)param; + } + } + else if (params instanceof RC5ParameterSpec) + { + RC5ParameterSpec rc5Param = (RC5ParameterSpec)params; + + param = new RC5Parameters(key.getEncoded(), ((RC5ParameterSpec)params).getRounds()); + if (baseEngine.getAlgorithmName().startsWith("RC5")) + { + if (baseEngine.getAlgorithmName().equals("RC5-32")) + { + if (rc5Param.getWordSize() != 32) + { + throw new InvalidAlgorithmParameterException("RC5 already set up for a word size of 32 not " + rc5Param.getWordSize() + "."); + } + } + else if (baseEngine.getAlgorithmName().equals("RC5-64")) + { + if (rc5Param.getWordSize() != 64) + { + throw new InvalidAlgorithmParameterException("RC5 already set up for a word size of 64 not " + rc5Param.getWordSize() + "."); + } + } + } + else + { + throw new InvalidAlgorithmParameterException("RC5 parameters passed to a cipher that is not RC5."); + } + if ((rc5Param.getIV() != null) && (ivLength != 0)) + { + param = new ParametersWithIV(param, rc5Param.getIV()); + ivParam = (ParametersWithIV)param; + } + } + */ + else + { + throw new InvalidAlgorithmParameterException("unknown parameter type."); + } + + if ((ivLength != 0) && !(param instanceof ParametersWithIV)) + { + SecureRandom ivRandom = random; + + if (ivRandom == null) + { + ivRandom = new SecureRandom(); + } + + if ((opmode == Cipher.ENCRYPT_MODE) || (opmode == Cipher.WRAP_MODE)) + { + byte[] iv = new byte[ivLength]; + + ivRandom.nextBytes(iv); + param = new ParametersWithIV(param, iv); + ivParam = (ParametersWithIV)param; + } + else if (cipher.getUnderlyingCipher().getAlgorithmName().indexOf("PGPCFB") < 0) + { + throw new InvalidAlgorithmParameterException("no IV set when one expected"); + } + } + + if (random != null && padded) + { + param = new ParametersWithRandom(param, random); + } + + try + { + switch (opmode) + { + case Cipher.ENCRYPT_MODE: + case Cipher.WRAP_MODE: + cipher.init(true, param); + break; + case Cipher.DECRYPT_MODE: + case Cipher.UNWRAP_MODE: + cipher.init(false, param); + break; + default: + throw new InvalidParameterException("unknown opmode " + opmode + " passed"); + } + } + catch (Exception e) + { + throw new InvalidKeyException(e.getMessage()); + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameters params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + AlgorithmParameterSpec paramSpec = null; + + if (params != null) + { + for (int i = 0; i != availableSpecs.length; i++) + { + try + { + paramSpec = params.getParameterSpec(availableSpecs[i]); + break; + } + catch (Exception e) + { + // try again if possible + } + } + + if (paramSpec == null) + { + throw new InvalidAlgorithmParameterException("can't handle parameter " + params.toString()); + } + } + + engineInit(opmode, key, paramSpec, random); + + engineParams = params; + } + + protected void engineInit( + int opmode, + Key key, + SecureRandom random) + throws InvalidKeyException + { + try + { + engineInit(opmode, key, (AlgorithmParameterSpec)null, random); + } + catch (InvalidAlgorithmParameterException e) + { + throw new InvalidKeyException(e.getMessage()); + } + } + + protected byte[] engineUpdate( + byte[] input, + int inputOffset, + int inputLen) + { + int length = cipher.getUpdateOutputSize(inputLen); + + if (length > 0) + { + byte[] out = new byte[length]; + + int len = cipher.processBytes(input, inputOffset, inputLen, out, 0); + + if (len == 0) + { + return null; + } + else if (len != out.length) + { + byte[] tmp = new byte[len]; + + System.arraycopy(out, 0, tmp, 0, len); + + return tmp; + } + + return out; + } + + cipher.processBytes(input, inputOffset, inputLen, null, 0); + + return null; + } + + protected int engineUpdate( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + throws ShortBufferException + { + try + { + return cipher.processBytes(input, inputOffset, inputLen, output, outputOffset); + } + catch (DataLengthException e) + { + throw new ShortBufferException(e.getMessage()); + } + } + + protected byte[] engineDoFinal( + byte[] input, + int inputOffset, + int inputLen) + throws IllegalBlockSizeException, BadPaddingException + { + int len = 0; + byte[] tmp = new byte[engineGetOutputSize(inputLen)]; + + if (inputLen != 0) + { + len = cipher.processBytes(input, inputOffset, inputLen, tmp, 0); + } + + try + { + len += cipher.doFinal(tmp, len); + } + catch (DataLengthException e) + { + throw new IllegalBlockSizeException(e.getMessage()); + } + catch (InvalidCipherTextException e) + { + throw new BadPaddingException(e.getMessage()); + } + + if (len == tmp.length) + { + return tmp; + } + + byte[] out = new byte[len]; + + System.arraycopy(tmp, 0, out, 0, len); + + return out; + } + + protected int engineDoFinal( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + throws IllegalBlockSizeException, BadPaddingException + { + int len = 0; + + if (inputLen != 0) + { + len = cipher.processBytes(input, inputOffset, inputLen, output, outputOffset); + } + + try + { + return (len + cipher.doFinal(output, outputOffset + len)); + } + catch (DataLengthException e) + { + throw new IllegalBlockSizeException(e.getMessage()); + } + catch (InvalidCipherTextException e) + { + throw new BadPaddingException(e.getMessage()); + } + } + + private boolean isAEADModeName( + String modeName) + { + return "CCM".equals(modeName) || "EAX".equals(modeName) || "GCM".equals(modeName); + } + + /* + * The ciphers that inherit from us. + */ + + /** + * DES + */ + /* + static public class DES + extends JCEBlockCipher + { + public DES() + { + super(new DESEngine()); + } + } + */ + + /** + * DESCBC + */ + /* + static public class DESCBC + extends JCEBlockCipher + { + public DESCBC() + { + super(new CBCBlockCipher(new DESEngine()), 64); + } + } + */ + + /** + * DESede + */ + /* + static public class DESede + extends JCEBlockCipher + { + public DESede() + { + super(new DESedeEngine()); + } + } + */ + + /** + * DESedeCBC + */ + /* + static public class DESedeCBC + extends JCEBlockCipher + { + public DESedeCBC() + { + super(new CBCBlockCipher(new DESedeEngine()), 64); + } + } + */ + + /** + * GOST28147 + */ + /* + static public class GOST28147 + extends JCEBlockCipher + { + public GOST28147() + { + super(new GOST28147Engine()); + } + } + + static public class GOST28147cbc + extends JCEBlockCipher + { + public GOST28147cbc() + { + super(new CBCBlockCipher(new GOST28147Engine()), 64); + } + } + */ + + /** + * SKIPJACK + */ + /* + static public class Skipjack + extends JCEBlockCipher + { + public Skipjack() + { + super(new SkipjackEngine()); + } + } + */ + + /** + * Blowfish + */ + /* + static public class Blowfish + extends JCEBlockCipher + { + public Blowfish() + { + super(new BlowfishEngine()); + } + } + */ + + /** + * Blowfish CBC + */ + /* + static public class BlowfishCBC + extends JCEBlockCipher + { + public BlowfishCBC() + { + super(new CBCBlockCipher(new BlowfishEngine()), 64); + } + } + */ + + /** + * Twofish + */ + static public class Twofish + extends JCEBlockCipher + { + public Twofish() + { + super(new TwofishEngine()); + } + } + + /** + * RC2 + */ + /* + static public class RC2 + extends JCEBlockCipher + { + public RC2() + { + super(new RC2Engine()); + } + } + */ + + /** + * RC2CBC + */ + /* + static public class RC2CBC + extends JCEBlockCipher + { + public RC2CBC() + { + super(new CBCBlockCipher(new RC2Engine()), 64); + } + } + */ + + /** + * RC5 + */ + /* + static public class RC5 + extends JCEBlockCipher + { + public RC5() + { + super(new RC532Engine()); + } + } + */ + + /** + * RC564 + */ + /* + static public class RC564 + extends JCEBlockCipher + { + public RC564() + { + super(new RC564Engine()); + } + } + */ + /** + * RC6 + */ + /* + static public class RC6 + extends JCEBlockCipher + { + public RC6() + { + super(new RC6Engine()); + } + } + + /** + * AES + */ + /* + static public class AES + extends JCEBlockCipher + { + public AES() + { + super(new AESFastEngine()); + } + } + + /** + * AESCBC + */ + /* + static public class AESCBC + extends JCEBlockCipher + { + public AESCBC() + { + super(new CBCBlockCipher(new AESFastEngine()), 128); + } + } + + /** + * AESCFB + */ + /* + static public class AESCFB + extends JCEBlockCipher + { + public AESCFB() + { + super(new CFBBlockCipher(new AESFastEngine(), 128), 128); + } + } + + /** + * AESOFB + */ + /* + static public class AESOFB + extends JCEBlockCipher + { + public AESOFB() + { + super(new OFBBlockCipher(new AESFastEngine(), 128), 128); + } + } + + /** + * Rijndael + */ + /* + static public class Rijndael + extends JCEBlockCipher + { + public Rijndael() + { + super(new RijndaelEngine()); + } + } + + /** + * Serpent + */ + /* + static public class Serpent + extends JCEBlockCipher + { + public Serpent() + { + super(new SerpentEngine()); + } + } + + + + /** + * CAST5 + */ + /* + static public class CAST5 + extends JCEBlockCipher + { + public CAST5() + { + super(new CAST5Engine()); + } + } + + /** + * CAST5 CBC + */ + /* + static public class CAST5CBC + extends JCEBlockCipher + { + public CAST5CBC() + { + super(new CBCBlockCipher(new CAST5Engine()), 64); + } + } + + /** + * CAST6 + */ + /* + static public class CAST6 + extends JCEBlockCipher + { + public CAST6() + { + super(new CAST6Engine()); + } + } + + /** + * TEA + */ + /* + static public class TEA + extends JCEBlockCipher + { + public TEA() + { + super(new TEAEngine()); + } + } + + /** + * XTEA + */ + /* + static public class XTEA + extends JCEBlockCipher + { + public XTEA() + { + super(new XTEAEngine()); + } + } + + /** + * SEED + */ + /* + static public class SEED + extends JCEBlockCipher + { + public SEED() + { + super(new SEEDEngine()); + } + } + + /** + * PBEWithMD5AndDES + */ + /* + static public class PBEWithMD5AndDES + extends JCEBlockCipher + { + public PBEWithMD5AndDES() + { + super(new CBCBlockCipher(new DESEngine())); + } + } + + /** + * PBEWithMD5AndRC2 + */ + /* + static public class PBEWithMD5AndRC2 + extends JCEBlockCipher + { + public PBEWithMD5AndRC2() + { + super(new CBCBlockCipher(new RC2Engine())); + } + } + + /** + * PBEWithSHA1AndDES + */ + /* + static public class PBEWithSHA1AndDES + extends JCEBlockCipher + { + public PBEWithSHA1AndDES() + { + super(new CBCBlockCipher(new DESEngine())); + } + } + + /** + * PBEWithSHA1AndRC2 + */ + /* + static public class PBEWithSHA1AndRC2 + extends JCEBlockCipher + { + public PBEWithSHA1AndRC2() + { + super(new CBCBlockCipher(new RC2Engine())); + } + } + + /** + * PBEWithSHAAnd3-KeyTripleDES-CBC + */ + /* + static public class PBEWithSHAAndDES3Key + extends JCEBlockCipher + { + public PBEWithSHAAndDES3Key() + { + super(new CBCBlockCipher(new DESedeEngine())); + } + } + + /** + * PBEWithSHAAnd2-KeyTripleDES-CBC + */ + /* + static public class PBEWithSHAAndDES2Key + extends JCEBlockCipher + { + public PBEWithSHAAndDES2Key() + { + super(new CBCBlockCipher(new DESedeEngine())); + } + } + + /** + * PBEWithSHAAnd128BitRC2-CBC + */ + /* + static public class PBEWithSHAAnd128BitRC2 + extends JCEBlockCipher + { + public PBEWithSHAAnd128BitRC2() + { + super(new CBCBlockCipher(new RC2Engine())); + } + } + + /** + * PBEWithSHAAnd40BitRC2-CBC + */ + /* + static public class PBEWithSHAAnd40BitRC2 + extends JCEBlockCipher + { + public PBEWithSHAAnd40BitRC2() + { + super(new CBCBlockCipher(new RC2Engine())); + } + } + + /** + * PBEWithSHAAndTwofish-CBC + */ + + static public class PBEWithSHAAndTwofish + extends JCEBlockCipher + { + public PBEWithSHAAndTwofish() + { + super(new CBCBlockCipher(new TwofishEngine())); + } + } + + /** + * PBEWithAES-CBC + */ + /* + static public class PBEWithAESCBC + extends JCEBlockCipher + { + public PBEWithAESCBC() + { + super(new CBCBlockCipher(new AESFastEngine())); + } + } + */ + + static private interface GenericBlockCipher + { + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException; + + public boolean wrapOnNoPadding(); + + public String getAlgorithmName(); + + public BlockCipher getUnderlyingCipher(); + + public int getOutputSize(int len); + + public int getUpdateOutputSize(int len); + + public int processByte(byte in, byte[] out, int outOff) + throws DataLengthException; + + public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) + throws DataLengthException; + + public int doFinal(byte[] out, int outOff) + throws IllegalStateException, InvalidCipherTextException; + } + + private static class BufferedGenericBlockCipher + implements GenericBlockCipher + { + private BufferedBlockCipher cipher; + + BufferedGenericBlockCipher(BufferedBlockCipher cipher) + { + this.cipher = cipher; + } + + BufferedGenericBlockCipher(BlockCipher cipher) + { + this.cipher = new PaddedBufferedBlockCipher(cipher); + } + + /* + BufferedGenericBlockCipher(BlockCipher cipher, BlockCipherPadding padding) + { + this.cipher = new PaddedBufferedBlockCipher(cipher, padding); + } + */ + + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException + { + cipher.init(forEncryption, params); + } + + public boolean wrapOnNoPadding() + { + return !(cipher instanceof CTSBlockCipher); + } + + public String getAlgorithmName() + { + return cipher.getUnderlyingCipher().getAlgorithmName(); + } + + public BlockCipher getUnderlyingCipher() + { + return cipher.getUnderlyingCipher(); + } + + public int getOutputSize(int len) + { + return cipher.getOutputSize(len); + } + + public int getUpdateOutputSize(int len) + { + return cipher.getUpdateOutputSize(len); + } + + public int processByte(byte in, byte[] out, int outOff) throws DataLengthException + { + return cipher.processByte(in, out, outOff); + } + + public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) throws DataLengthException + { + return cipher.processBytes(in, inOff, len, out, outOff); + } + + public int doFinal(byte[] out, int outOff) throws IllegalStateException, InvalidCipherTextException + { + return cipher.doFinal(out, outOff); + } + } + + /* + private static class AEADGenericBlockCipher + implements GenericBlockCipher + { + private AEADBlockCipher cipher; + + AEADGenericBlockCipher(AEADBlockCipher cipher) + { + this.cipher = cipher; + } + + public void init(boolean forEncryption, CipherParameters params) + throws IllegalArgumentException + { + cipher.init(forEncryption, params); + } + + public String getAlgorithmName() + { + return cipher.getUnderlyingCipher().getAlgorithmName(); + } + + public boolean wrapOnNoPadding() + { + return false; + } + + public BlockCipher getUnderlyingCipher() + { + return cipher.getUnderlyingCipher(); + } + + public int getOutputSize(int len) + { + return cipher.getOutputSize(len); + } + + public int getUpdateOutputSize(int len) + { + return cipher.getUpdateOutputSize(len); + } + + public int processByte(byte in, byte[] out, int outOff) throws DataLengthException + { + return cipher.processByte(in, out, outOff); + } + + public int processBytes(byte[] in, int inOff, int len, byte[] out, int outOff) throws DataLengthException + { + return cipher.processBytes(in, inOff, len, out, outOff); + } + + public int doFinal(byte[] out, int outOff) throws IllegalStateException, InvalidCipherTextException + { + return cipher.doFinal(out, outOff); + } + } + */ +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/provider/JCEPBEKey.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/provider/JCEPBEKey.java new file mode 100644 index 00000000..810a9e94 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/provider/JCEPBEKey.java @@ -0,0 +1,152 @@ +package org.bouncycastle.jce.provider; + +import javax.crypto.interfaces.PBEKey; +import javax.crypto.spec.PBEKeySpec; + +import org.bouncycastle.asn1.DERObjectIdentifier; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.PBEParametersGenerator; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; + +@SuppressWarnings("serial") +public class JCEPBEKey + implements PBEKey +{ + String algorithm; + DERObjectIdentifier oid; + int type; + int digest; + int keySize; + int ivSize; + CipherParameters param; + PBEKeySpec pbeKeySpec; + boolean tryWrong = false; + + /** + * @param param + */ + public JCEPBEKey( + String algorithm, + DERObjectIdentifier oid, + int type, + int digest, + int keySize, + int ivSize, + PBEKeySpec pbeKeySpec, + CipherParameters param) + { + this.algorithm = algorithm; + this.oid = oid; + this.type = type; + this.digest = digest; + this.keySize = keySize; + this.ivSize = ivSize; + this.pbeKeySpec = pbeKeySpec; + this.param = param; + } + + public String getAlgorithm() + { + return algorithm; + } + + public String getFormat() + { + return "RAW"; + } + + public byte[] getEncoded() + { + if (param != null) + { + KeyParameter kParam; + + if (param instanceof ParametersWithIV) + { + kParam = (KeyParameter)((ParametersWithIV)param).getParameters(); + } + else + { + kParam = (KeyParameter)param; + } + + return kParam.getKey(); + } + else + { + if (type == PBE.PKCS12) + { + return PBEParametersGenerator.PKCS12PasswordToBytes(pbeKeySpec.getPassword()); + } + else + { + return PBEParametersGenerator.PKCS5PasswordToBytes(pbeKeySpec.getPassword()); + } + } + } + + int getType() + { + return type; + } + + int getDigest() + { + return digest; + } + + int getKeySize() + { + return keySize; + } + + int getIvSize() + { + return ivSize; + } + + CipherParameters getParam() + { + return param; + } + + /* (non-Javadoc) + * @see javax.crypto.interfaces.PBEKey#getPassword() + */ + public char[] getPassword() + { + return pbeKeySpec.getPassword(); + } + + /* (non-Javadoc) + * @see javax.crypto.interfaces.PBEKey#getSalt() + */ + public byte[] getSalt() + { + return pbeKeySpec.getSalt(); + } + + /* (non-Javadoc) + * @see javax.crypto.interfaces.PBEKey#getIterationCount() + */ + public int getIterationCount() + { + return pbeKeySpec.getIterationCount(); + } + + public DERObjectIdentifier getOID() + { + return oid; + } + + void setTryWrongPKCS12Zero(boolean tryWrong) + { + this.tryWrong = tryWrong; + } + + boolean shouldTryWrongPKCS12() + { + return tryWrong; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/provider/JCEStreamCipher.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/provider/JCEStreamCipher.java new file mode 100644 index 00000000..d366c10b --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/provider/JCEStreamCipher.java @@ -0,0 +1,622 @@ +package org.bouncycastle.jce.provider; + +import org.bouncycastle.crypto.BlockCipher; +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.DataLengthException; +import org.bouncycastle.crypto.StreamBlockCipher; +import org.bouncycastle.crypto.StreamCipher; +//import org.bouncycastle.crypto.engines.BlowfishEngine; +//import org.bouncycastle.crypto.engines.DESEngine; +//import org.bouncycastle.crypto.engines.DESedeEngine; +//import org.bouncycastle.crypto.engines.HC128Engine; +//import org.bouncycastle.crypto.engines.HC256Engine; +//import org.bouncycastle.crypto.engines.RC4Engine; +import org.bouncycastle.crypto.engines.Salsa20Engine; +//import org.bouncycastle.crypto.engines.SkipjackEngine; +import org.bouncycastle.crypto.engines.TwofishEngine; +//import org.bouncycastle.crypto.engines.VMPCEngine; +//import org.bouncycastle.crypto.engines.VMPCKSA3Engine; +import org.bouncycastle.crypto.modes.CFBBlockCipher; +import org.bouncycastle.crypto.modes.OFBBlockCipher; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; + +import javax.crypto.Cipher; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SecretKey; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEParameterSpec; +import javax.crypto.spec.RC2ParameterSpec; +import javax.crypto.spec.RC5ParameterSpec; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; + +@SuppressWarnings("unchecked") +public class JCEStreamCipher + extends WrapCipherSpi implements PBE +{ + // + // specs we can handle. + // + private Class[] availableSpecs = + { + RC2ParameterSpec.class, + RC5ParameterSpec.class, + IvParameterSpec.class, + PBEParameterSpec.class + }; + + private StreamCipher cipher; + private ParametersWithIV ivParam; + + private int ivLength = 0; + + private PBEParameterSpec pbeSpec = null; + private String pbeAlgorithm = null; + + protected JCEStreamCipher( + StreamCipher engine, + int ivLength) + { + cipher = engine; + this.ivLength = ivLength; + } + + protected JCEStreamCipher( + BlockCipher engine, + int ivLength) + { + this.ivLength = ivLength; + + cipher = new StreamBlockCipher(engine); + } + + protected int engineGetBlockSize() + { + return 0; + } + + protected byte[] engineGetIV() + { + return (ivParam != null) ? ivParam.getIV() : null; + } + + protected int engineGetKeySize( + Key key) + { + return key.getEncoded().length * 8; + } + + protected int engineGetOutputSize( + int inputLen) + { + return inputLen; + } + + protected AlgorithmParameters engineGetParameters() + { + if (engineParams == null) + { + if (pbeSpec != null) + { + try + { + AlgorithmParameters engineParams = AlgorithmParameters.getInstance(pbeAlgorithm, "BC"); + engineParams.init(pbeSpec); + + return engineParams; + } + catch (Exception e) + { + return null; + } + } + } + + return engineParams; + } + + /** + * should never be called. + */ + protected void engineSetMode( + String mode) + { + if (!mode.equalsIgnoreCase("ECB")) + { + throw new IllegalArgumentException("can't support mode " + mode); + } + } + + /** + * should never be called. + */ + protected void engineSetPadding( + String padding) + throws NoSuchPaddingException + { + if (!padding.equalsIgnoreCase("NoPadding")) + { + throw new NoSuchPaddingException("Padding " + padding + " unknown."); + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + CipherParameters param; + + this.pbeSpec = null; + this.pbeAlgorithm = null; + + this.engineParams = null; + + // + // basic key check + // + if (!(key instanceof SecretKey)) + { + throw new InvalidKeyException("Key for algorithm " + key.getAlgorithm() + " not suitable for symmetric enryption."); + } + + if (key instanceof JCEPBEKey) + { + JCEPBEKey k = (JCEPBEKey)key; + + if (k.getOID() != null) + { + pbeAlgorithm = k.getOID().getId(); + } + else + { + pbeAlgorithm = k.getAlgorithm(); + } + + if (k.getParam() != null) + { + param = k.getParam(); + pbeSpec = new PBEParameterSpec(k.getSalt(), k.getIterationCount()); + } + else if (params instanceof PBEParameterSpec) + { + param = PBE.Util.makePBEParameters(k, params, cipher.getAlgorithmName()); + pbeSpec = (PBEParameterSpec)params; + } + else + { + throw new InvalidAlgorithmParameterException("PBE requires PBE parameters to be set."); + } + + if (k.getIvSize() != 0) + { + ivParam = (ParametersWithIV)param; + } + } + else if (params == null) + { + param = new KeyParameter(key.getEncoded()); + } + else if (params instanceof IvParameterSpec) + { + param = new ParametersWithIV(new KeyParameter(key.getEncoded()), ((IvParameterSpec)params).getIV()); + ivParam = (ParametersWithIV)param; + } + else + { + throw new IllegalArgumentException("unknown parameter type."); + } + + if ((ivLength != 0) && !(param instanceof ParametersWithIV)) + { + SecureRandom ivRandom = random; + + if (ivRandom == null) + { + ivRandom = new SecureRandom(); + } + + if ((opmode == Cipher.ENCRYPT_MODE) || (opmode == Cipher.WRAP_MODE)) + { + byte[] iv = new byte[ivLength]; + + ivRandom.nextBytes(iv); + param = new ParametersWithIV(param, iv); + ivParam = (ParametersWithIV)param; + } + else + { + throw new InvalidAlgorithmParameterException("no IV set when one expected"); + } + } + + switch (opmode) + { + case Cipher.ENCRYPT_MODE: + case Cipher.WRAP_MODE: + cipher.init(true, param); + break; + case Cipher.DECRYPT_MODE: + case Cipher.UNWRAP_MODE: + cipher.init(false, param); + break; + default: + System.out.println("eeek!"); + } + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameters params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + AlgorithmParameterSpec paramSpec = null; + + if (params != null) + { + for (int i = 0; i != availableSpecs.length; i++) + { + try + { + paramSpec = params.getParameterSpec(availableSpecs[i]); + break; + } + catch (Exception e) + { + continue; + } + } + + if (paramSpec == null) + { + throw new InvalidAlgorithmParameterException("can't handle parameter " + params.toString()); + } + } + + engineInit(opmode, key, paramSpec, random); + engineParams = params; + } + + protected void engineInit( + int opmode, + Key key, + SecureRandom random) + throws InvalidKeyException + { + try + { + engineInit(opmode, key, (AlgorithmParameterSpec)null, random); + } + catch (InvalidAlgorithmParameterException e) + { + throw new InvalidKeyException(e.getMessage()); + } + } + + protected byte[] engineUpdate( + byte[] input, + int inputOffset, + int inputLen) + { + byte[] out = new byte[inputLen]; + + cipher.processBytes(input, inputOffset, inputLen, out, 0); + + return out; + } + + protected int engineUpdate( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + throws ShortBufferException + { + try + { + cipher.processBytes(input, inputOffset, inputLen, output, outputOffset); + + return inputLen; + } + catch (DataLengthException e) + { + throw new ShortBufferException(e.getMessage()); + } + } + + protected byte[] engineDoFinal( + byte[] input, + int inputOffset, + int inputLen) + { + if (inputLen != 0) + { + byte[] out = engineUpdate(input, inputOffset, inputLen); + + cipher.reset(); + + return out; + } + + cipher.reset(); + + return new byte[0]; + } + + protected int engineDoFinal( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + { + if (inputLen != 0) + { + cipher.processBytes(input, inputOffset, inputLen, output, outputOffset); + } + + cipher.reset(); + + return inputLen; + } + + /* + * The ciphers that inherit from us. + */ + + /** + * DES + */ + /* + static public class DES_CFB8 + extends JCEStreamCipher + { + public DES_CFB8() + { + super(new CFBBlockCipher(new DESEngine(), 8), 64); + } + } + */ + + /** + * DESede + */ + /* + static public class DESede_CFB8 + extends JCEStreamCipher + { + public DESede_CFB8() + { + super(new CFBBlockCipher(new DESedeEngine(), 8), 64); + } + } + */ + + /** + * SKIPJACK + */ + /* + static public class Skipjack_CFB8 + extends JCEStreamCipher + { + public Skipjack_CFB8() + { + super(new CFBBlockCipher(new SkipjackEngine(), 8), 64); + } + } + */ + + /** + * Blowfish + */ + /* + static public class Blowfish_CFB8 + extends JCEStreamCipher + { + public Blowfish_CFB8() + { + super(new CFBBlockCipher(new BlowfishEngine(), 8), 64); + } + } + */ + + /** + * Twofish + */ + static public class Twofish_CFB8 + extends JCEStreamCipher + { + public Twofish_CFB8() + { + super(new CFBBlockCipher(new TwofishEngine(), 8), 128); + } + } + + /** + * DES + */ + /* + static public class DES_OFB8 + extends JCEStreamCipher + { + public DES_OFB8() + { + super(new OFBBlockCipher(new DESEngine(), 8), 64); + } + } + */ + + /** + * DESede + */ + /* + static public class DESede_OFB8 + extends JCEStreamCipher + { + public DESede_OFB8() + { + super(new OFBBlockCipher(new DESedeEngine(), 8), 64); + } + } + */ + + /** + * SKIPJACK + */ + /* + static public class Skipjack_OFB8 + extends JCEStreamCipher + { + public Skipjack_OFB8() + { + super(new OFBBlockCipher(new SkipjackEngine(), 8), 64); + } + } + */ + + /** + * Blowfish + */ + /* + static public class Blowfish_OFB8 + extends JCEStreamCipher + { + public Blowfish_OFB8() + { + super(new OFBBlockCipher(new BlowfishEngine(), 8), 64); + } + } + */ + + /** + * Twofish + */ + static public class Twofish_OFB8 + extends JCEStreamCipher + { + public Twofish_OFB8() + { + super(new OFBBlockCipher(new TwofishEngine(), 8), 128); + } + } + + /** + * RC4 + */ + /* + static public class RC4 + extends JCEStreamCipher + { + public RC4() + { + super(new RC4Engine(), 0); + } + } + */ + + /** + * PBEWithSHAAnd128BitRC4 + */ + /* + static public class PBEWithSHAAnd128BitRC4 + extends JCEStreamCipher + { + public PBEWithSHAAnd128BitRC4() + { + super(new RC4Engine(), 0); + } + } + */ + /** + * PBEWithSHAAnd40BitRC4 + */ + /* + static public class PBEWithSHAAnd40BitRC4 + extends JCEStreamCipher + { + public PBEWithSHAAnd40BitRC4() + { + super(new RC4Engine(), 0); + } + } + */ + + /** + * Salsa20 + */ + static public class Salsa20 + extends JCEStreamCipher + { + public Salsa20() + { + super(new Salsa20Engine(), 8); + } + } + + /** + * HC-128 + */ + /* + static public class HC128 + extends JCEStreamCipher + { + public HC128() + { + super(new HC128Engine(), 16); + } + } + */ + + /** + * HC-256 + */ + /* + static public class HC256 + extends JCEStreamCipher + { + public HC256() + { + super(new HC256Engine(), 32); + } + } + */ + + /** + * VMPC + */ + /* + static public class VMPC + extends JCEStreamCipher + { + public VMPC() + { + super(new VMPCEngine(), 16); + } + } + */ + + /** + * VMPC-KSA3 + */ + /* + static public class VMPCKSA3 + extends JCEStreamCipher + { + public VMPCKSA3() + { + super(new VMPCKSA3Engine(), 16); + } + } + */ +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/provider/PBE.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/provider/PBE.java new file mode 100644 index 00000000..7bd1600f --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/provider/PBE.java @@ -0,0 +1,281 @@ +package org.bouncycastle.jce.provider; + +import java.security.spec.AlgorithmParameterSpec; + +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.PBEParameterSpec; + +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.PBEParametersGenerator; +import org.bouncycastle.crypto.digests.MD2Digest; +import org.bouncycastle.crypto.digests.MD5Digest; +import org.bouncycastle.crypto.digests.RIPEMD160Digest; +import org.bouncycastle.crypto.digests.SHA1Digest; +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.digests.TigerDigest; +import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator; +import org.bouncycastle.crypto.generators.PKCS12ParametersGenerator; +import org.bouncycastle.crypto.generators.PKCS5S1ParametersGenerator; +import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator; +import org.bouncycastle.crypto.params.DESParameters; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; + +public interface PBE +{ + // + // PBE Based encryption constants - by default we do PKCS12 with SHA-1 + // + static final int MD5 = 0; + static final int SHA1 = 1; + static final int RIPEMD160 = 2; + static final int TIGER = 3; + static final int SHA256 = 4; + static final int MD2 = 5; + + static final int PKCS5S1 = 0; + static final int PKCS5S2 = 1; + static final int PKCS12 = 2; + static final int OPENSSL = 3; + + /** + * uses the appropriate mixer to generate the key and IV if necessary. + */ + static class Util + { + static private PBEParametersGenerator makePBEGenerator( + int type, + int hash) + { + PBEParametersGenerator generator; + + if (type == PKCS5S1) + { + switch (hash) + { + case MD2: + generator = new PKCS5S1ParametersGenerator(new MD2Digest()); + break; + case MD5: + generator = new PKCS5S1ParametersGenerator(new MD5Digest()); + break; + case SHA1: + generator = new PKCS5S1ParametersGenerator(new SHA1Digest()); + break; + default: + throw new IllegalStateException("PKCS5 scheme 1 only supports MD2, MD5 and SHA1."); + } + } + else if (type == PKCS5S2) + { + generator = new PKCS5S2ParametersGenerator(); + } + else if (type == PKCS12) + { + switch (hash) + { + case MD2: + generator = new PKCS12ParametersGenerator(new MD2Digest()); + break; + case MD5: + generator = new PKCS12ParametersGenerator(new MD5Digest()); + break; + case SHA1: + generator = new PKCS12ParametersGenerator(new SHA1Digest()); + break; + case RIPEMD160: + generator = new PKCS12ParametersGenerator(new RIPEMD160Digest()); + break; + case TIGER: + generator = new PKCS12ParametersGenerator(new TigerDigest()); + break; + case SHA256: + generator = new PKCS12ParametersGenerator(new SHA256Digest()); + break; + default: + throw new IllegalStateException("unknown digest scheme for PBE encryption."); + } + } + else + { + generator = new OpenSSLPBEParametersGenerator(); + } + + return generator; + } + + /** + * construct a key and iv (if necessary) suitable for use with a + * Cipher. + */ + static CipherParameters makePBEParameters( + JCEPBEKey pbeKey, + AlgorithmParameterSpec spec, + String targetAlgorithm) + { + if ((spec == null) || !(spec instanceof PBEParameterSpec)) + { + throw new IllegalArgumentException("Need a PBEParameter spec with a PBE key."); + } + + PBEParameterSpec pbeParam = (PBEParameterSpec)spec; + PBEParametersGenerator generator = makePBEGenerator(pbeKey.getType(), pbeKey.getDigest()); + byte[] key = pbeKey.getEncoded(); + CipherParameters param; + + if (pbeKey.shouldTryWrongPKCS12()) + { + key = new byte[2]; + } + + generator.init(key, pbeParam.getSalt(), pbeParam.getIterationCount()); + + if (pbeKey.getIvSize() != 0) + { + param = generator.generateDerivedParameters(pbeKey.getKeySize(), pbeKey.getIvSize()); + } + else + { + param = generator.generateDerivedParameters(pbeKey.getKeySize()); + } + + if (targetAlgorithm.startsWith("DES")) + { + if (param instanceof ParametersWithIV) + { + KeyParameter kParam = (KeyParameter)((ParametersWithIV)param).getParameters(); + + DESParameters.setOddParity(kParam.getKey()); + } + else + { + KeyParameter kParam = (KeyParameter)param; + + DESParameters.setOddParity(kParam.getKey()); + } + } + + for (int i = 0; i != key.length; i++) + { + key[i] = 0; + } + + return param; + } + + /** + * generate a PBE based key suitable for a MAC algorithm, the + * key size is chosen according the MAC size, or the hashing algorithm, + * whichever is greater. + */ + static CipherParameters makePBEMacParameters( + JCEPBEKey pbeKey, + AlgorithmParameterSpec spec) + { + if ((spec == null) || !(spec instanceof PBEParameterSpec)) + { + throw new IllegalArgumentException("Need a PBEParameter spec with a PBE key."); + } + + PBEParameterSpec pbeParam = (PBEParameterSpec)spec; + PBEParametersGenerator generator = makePBEGenerator(pbeKey.getType(), pbeKey.getDigest()); + byte[] key = pbeKey.getEncoded(); + CipherParameters param; + + if (pbeKey.shouldTryWrongPKCS12()) + { + key = new byte[2]; + } + + generator.init(key, pbeParam.getSalt(), pbeParam.getIterationCount()); + + param = generator.generateDerivedMacParameters(pbeKey.getKeySize()); + + for (int i = 0; i != key.length; i++) + { + key[i] = 0; + } + + return param; + } + + /** + * construct a key and iv (if necessary) suitable for use with a + * Cipher. + */ + static CipherParameters makePBEParameters( + PBEKeySpec keySpec, + int type, + int hash, + int keySize, + int ivSize) + { + PBEParametersGenerator generator = makePBEGenerator(type, hash); + byte[] key; + CipherParameters param; + + if (type == PKCS12) + { + key = PBEParametersGenerator.PKCS12PasswordToBytes(keySpec.getPassword()); + } + else + { + key = PBEParametersGenerator.PKCS5PasswordToBytes(keySpec.getPassword()); + } + + generator.init(key, keySpec.getSalt(), keySpec.getIterationCount()); + + if (ivSize != 0) + { + param = generator.generateDerivedParameters(keySize, ivSize); + } + else + { + param = generator.generateDerivedParameters(keySize); + } + + for (int i = 0; i != key.length; i++) + { + key[i] = 0; + } + + return param; + } + + /** + * generate a PBE based key suitable for a MAC algorithm, the + * key size is chosen according the MAC size, or the hashing algorithm, + * whichever is greater. + */ + static CipherParameters makePBEMacParameters( + PBEKeySpec keySpec, + int type, + int hash, + int keySize) + { + PBEParametersGenerator generator = makePBEGenerator(type, hash); + byte[] key; + CipherParameters param; + + if (type == PKCS12) + { + key = PBEParametersGenerator.PKCS12PasswordToBytes(keySpec.getPassword()); + } + else + { + key = PBEParametersGenerator.PKCS5PasswordToBytes(keySpec.getPassword()); + } + + generator.init(key, keySpec.getSalt(), keySpec.getIterationCount()); + + param = generator.generateDerivedMacParameters(keySize); + + for (int i = 0; i != key.length; i++) + { + key[i] = 0; + } + + return param; + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/provider/PKCS12BagAttributeCarrierImpl.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/provider/PKCS12BagAttributeCarrierImpl.java new file mode 100644 index 00000000..9907b200 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/provider/PKCS12BagAttributeCarrierImpl.java @@ -0,0 +1,125 @@ +package org.bouncycastle.jce.provider; + +import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier; +import org.bouncycastle.asn1.DERObjectIdentifier; +import org.bouncycastle.asn1.DEREncodable; +import org.bouncycastle.asn1.ASN1OutputStream; +import org.bouncycastle.asn1.ASN1InputStream; + +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; +import java.io.ObjectOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; + +@SuppressWarnings("unchecked") +class PKCS12BagAttributeCarrierImpl + implements PKCS12BagAttributeCarrier +{ + private Hashtable pkcs12Attributes; + private Vector pkcs12Ordering; + + PKCS12BagAttributeCarrierImpl(Hashtable attributes, Vector ordering) + { + this.pkcs12Attributes = attributes; + this.pkcs12Ordering = ordering; + } + + public PKCS12BagAttributeCarrierImpl() + { + this(new Hashtable(), new Vector()); + } + + public void setBagAttribute( + DERObjectIdentifier oid, + DEREncodable attribute) + { + if (pkcs12Attributes.containsKey(oid)) + { // preserve original ordering + pkcs12Attributes.put(oid, attribute); + } + else + { + pkcs12Attributes.put(oid, attribute); + pkcs12Ordering.addElement(oid); + } + } + + public DEREncodable getBagAttribute( + DERObjectIdentifier oid) + { + return (DEREncodable)pkcs12Attributes.get(oid); + } + + public Enumeration getBagAttributeKeys() + { + return pkcs12Ordering.elements(); + } + + int size() + { + return pkcs12Ordering.size(); + } + + Hashtable getAttributes() + { + return pkcs12Attributes; + } + + Vector getOrdering() + { + return pkcs12Ordering; + } + + public void writeObject(ObjectOutputStream out) + throws IOException + { + if (pkcs12Ordering.size() == 0) + { + out.writeObject(new Hashtable()); + out.writeObject(new Vector()); + } + else + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + ASN1OutputStream aOut = new ASN1OutputStream(bOut); + + Enumeration e = this.getBagAttributeKeys(); + + while (e.hasMoreElements()) + { + DERObjectIdentifier oid = (DERObjectIdentifier)e.nextElement(); + + aOut.writeObject(oid); + aOut.writeObject(pkcs12Attributes.get(oid)); + } + + out.writeObject(bOut.toByteArray()); + } + } + + public void readObject(ObjectInputStream in) + throws IOException, ClassNotFoundException + { + Object obj = in.readObject(); + + if (obj instanceof Hashtable) + { + this.pkcs12Attributes = (Hashtable)obj; + this.pkcs12Ordering = (Vector)in.readObject(); + } + else + { + ASN1InputStream aIn = new ASN1InputStream((byte[])obj); + + DERObjectIdentifier oid; + + while ((oid = (DERObjectIdentifier)aIn.readObject()) != null) + { + this.setBagAttribute(oid, aIn.readObject()); + } + } + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/provider/WrapCipherSpi.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/provider/WrapCipherSpi.java new file mode 100644 index 00000000..090fbf13 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/jce/provider/WrapCipherSpi.java @@ -0,0 +1,461 @@ +package org.bouncycastle.jce.provider; + +/* +import org.bouncycastle.asn1.ASN1InputStream; +import org.bouncycastle.asn1.ASN1Sequence; +import org.bouncycastle.asn1.DERObjectIdentifier; +import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.asn1.x9.X9ObjectIdentifiers; +*/ +import org.bouncycastle.crypto.CipherParameters; +import org.bouncycastle.crypto.InvalidCipherTextException; +import org.bouncycastle.crypto.Wrapper; +//import org.bouncycastle.crypto.engines.DESedeEngine; +//import org.bouncycastle.crypto.engines.DESedeWrapEngine; +//import org.bouncycastle.crypto.engines.RC2WrapEngine; +//import org.bouncycastle.crypto.engines.RFC3211WrapEngine; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.crypto.params.ParametersWithIV; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.CipherSpi; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.ShortBufferException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEParameterSpec; +import javax.crypto.spec.RC2ParameterSpec; +import javax.crypto.spec.RC5ParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.security.AlgorithmParameters; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +//import java.security.PrivateKey; +import java.security.SecureRandom; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.security.spec.X509EncodedKeySpec; + +public abstract class WrapCipherSpi extends CipherSpi + implements PBE +{ + // + // specs we can handle. + // + @SuppressWarnings("unchecked") + private Class[] availableSpecs = + { + IvParameterSpec.class, + PBEParameterSpec.class, + RC2ParameterSpec.class, + RC5ParameterSpec.class + }; + + protected int pbeType = PKCS12; + protected int pbeHash = SHA1; + protected int pbeKeySize; + protected int pbeIvSize; + + protected AlgorithmParameters engineParams = null; + + protected Wrapper wrapEngine = null; + + private int ivSize; + private byte[] iv; + + protected WrapCipherSpi() + { + } + + protected WrapCipherSpi( + Wrapper wrapEngine) + { + this(wrapEngine, 0); + } + + protected WrapCipherSpi( + Wrapper wrapEngine, + int ivSize) + { + this.wrapEngine = wrapEngine; + this.ivSize = ivSize; + } + + protected int engineGetBlockSize() + { + return 0; + } + + protected byte[] engineGetIV() + { + return (byte[])iv.clone(); + } + + protected int engineGetKeySize( + Key key) + { + return key.getEncoded().length; + } + + protected int engineGetOutputSize( + int inputLen) + { + return -1; + } + + protected AlgorithmParameters engineGetParameters() + { + return null; + } + + protected void engineSetMode( + String mode) + throws NoSuchAlgorithmException + { + throw new NoSuchAlgorithmException("can't support mode " + mode); + } + + protected void engineSetPadding( + String padding) + throws NoSuchPaddingException + { + throw new NoSuchPaddingException("Padding " + padding + " unknown."); + } + + protected void engineInit( + int opmode, + Key key, + AlgorithmParameterSpec params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + CipherParameters param; + + if (key instanceof JCEPBEKey) + { + JCEPBEKey k = (JCEPBEKey)key; + + if (params instanceof PBEParameterSpec) + { + param = PBE.Util.makePBEParameters(k, params, wrapEngine.getAlgorithmName()); + } + else if (k.getParam() != null) + { + param = k.getParam(); + } + else + { + throw new InvalidAlgorithmParameterException("PBE requires PBE parameters to be set."); + } + } + else + { + param = new KeyParameter(key.getEncoded()); + } + + if (params instanceof javax.crypto.spec.IvParameterSpec) + { + IvParameterSpec iv = (IvParameterSpec) params; + param = new ParametersWithIV(param, iv.getIV()); + } + + if (param instanceof KeyParameter && ivSize != 0) + { + iv = new byte[ivSize]; + random.nextBytes(iv); + param = new ParametersWithIV(param, iv); + } + + switch (opmode) + { + case Cipher.WRAP_MODE: + wrapEngine.init(true, param); + break; + case Cipher.UNWRAP_MODE: + wrapEngine.init(false, param); + break; + case Cipher.ENCRYPT_MODE: + case Cipher.DECRYPT_MODE: + throw new IllegalArgumentException("engine only valid for wrapping"); + default: + System.out.println("eeek!"); + } + } + + @SuppressWarnings("unchecked") + protected void engineInit( + int opmode, + Key key, + AlgorithmParameters params, + SecureRandom random) + throws InvalidKeyException, InvalidAlgorithmParameterException + { + AlgorithmParameterSpec paramSpec = null; + + if (params != null) + { + for (int i = 0; i != availableSpecs.length; i++) + { + try + { + paramSpec = params.getParameterSpec(availableSpecs[i]); + break; + } + catch (Exception e) + { + // try next spec + } + } + + if (paramSpec == null) + { + throw new InvalidAlgorithmParameterException("can't handle parameter " + params.toString()); + } + } + + engineParams = params; + engineInit(opmode, key, paramSpec, random); + } + + protected void engineInit( + int opmode, + Key key, + SecureRandom random) + throws InvalidKeyException + { + try + { + engineInit(opmode, key, (AlgorithmParameterSpec)null, random); + } + catch (InvalidAlgorithmParameterException e) + { + throw new IllegalArgumentException(e.getMessage()); + } + } + + protected byte[] engineUpdate( + byte[] input, + int inputOffset, + int inputLen) + { + throw new RuntimeException("not supported for wrapping"); + } + + protected int engineUpdate( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + throws ShortBufferException + { + throw new RuntimeException("not supported for wrapping"); + } + + protected byte[] engineDoFinal( + byte[] input, + int inputOffset, + int inputLen) + throws IllegalBlockSizeException, BadPaddingException + { + return null; + } + + protected int engineDoFinal( + byte[] input, + int inputOffset, + int inputLen, + byte[] output, + int outputOffset) + throws IllegalBlockSizeException, BadPaddingException + { + return 0; + } + + protected byte[] engineWrap( + Key key) + throws IllegalBlockSizeException, java.security.InvalidKeyException + { + byte[] encoded = key.getEncoded(); + if (encoded == null) + { + throw new InvalidKeyException("Cannot wrap key, null encoding."); + } + + try + { + if (wrapEngine == null) + { + return engineDoFinal(encoded, 0, encoded.length); + } + else + { + return wrapEngine.wrap(encoded, 0, encoded.length); + } + } + catch (BadPaddingException e) + { + throw new IllegalBlockSizeException(e.getMessage()); + } + } + + protected Key engineUnwrap( + byte[] wrappedKey, + String wrappedKeyAlgorithm, + int wrappedKeyType) + throws InvalidKeyException + { + byte[] encoded; + try + { + if (wrapEngine == null) + { + encoded = engineDoFinal(wrappedKey, 0, wrappedKey.length); + } + else + { + encoded = wrapEngine.unwrap(wrappedKey, 0, wrappedKey.length); + } + } + catch (InvalidCipherTextException e) + { + throw new InvalidKeyException(e.getMessage()); + } + catch (BadPaddingException e) + { + throw new InvalidKeyException(e.getMessage()); + } + catch (IllegalBlockSizeException e2) + { + throw new InvalidKeyException(e2.getMessage()); + } + + if (wrappedKeyType == Cipher.SECRET_KEY) + { + return new SecretKeySpec(encoded, wrappedKeyAlgorithm); + } + /* + else if (wrappedKeyAlgorithm.equals("") && wrappedKeyType == Cipher.PRIVATE_KEY) + { + + // * The caller doesnt know the algorithm as it is part of + // * the encrypted data. + + ASN1InputStream bIn = new ASN1InputStream(encoded); + PrivateKey privKey; + + try + { + ASN1Sequence s = (ASN1Sequence)bIn.readObject(); + PrivateKeyInfo in = new PrivateKeyInfo(s); + + DERObjectIdentifier oid = in.getAlgorithmId().getObjectId(); + + + if (oid.equals(X9ObjectIdentifiers.id_ecPublicKey)) + { + privKey = new JCEECPrivateKey(in); + } + else if (oid.equals(CryptoProObjectIdentifiers.gostR3410_94)) + { + privKey = new JDKGOST3410PrivateKey(in); + } + + else if (oid.equals(X9ObjectIdentifiers.id_dsa)) + { + privKey = new JDKDSAPrivateKey(in); + } + else if (oid.equals(PKCSObjectIdentifiers.dhKeyAgreement)) + { + privKey = new JCEDHPrivateKey(in); + } + else if (oid.equals(X9ObjectIdentifiers.dhpublicnumber)) + { + privKey = new JCEDHPrivateKey(in); + } + else // the old standby! + { + privKey = new JCERSAPrivateCrtKey(in); + } + } + catch (Exception e) + { + throw new InvalidKeyException("Invalid key encoding."); + } + + return privKey; + } + */ + else + { + try + { + KeyFactory kf = KeyFactory.getInstance(wrappedKeyAlgorithm, "BC"); + if (wrappedKeyType == Cipher.PUBLIC_KEY) + { + return kf.generatePublic(new X509EncodedKeySpec(encoded)); + } + else if (wrappedKeyType == Cipher.PRIVATE_KEY) + { + return kf.generatePrivate(new PKCS8EncodedKeySpec(encoded)); + } + } + catch (NoSuchProviderException e) + { + throw new InvalidKeyException("Unknown key type " + e.getMessage()); + } + catch (NoSuchAlgorithmException e) + { + throw new InvalidKeyException("Unknown key type " + e.getMessage()); + } + catch (InvalidKeySpecException e2) + { + throw new InvalidKeyException("Unknown key type " + e2.getMessage()); + } + + throw new InvalidKeyException("Unknown key type " + wrappedKeyType); + } + } + + // + // classes that inherit directly from us + // + + + /* + public static class DESEDEWrap + extends WrapCipherSpi + { + public DESEDEWrap() + { + super(new DESedeWrapEngine()); + } + } + + public static class RC2Wrap + extends WrapCipherSpi + { + public RC2Wrap() + { + super(new RC2WrapEngine()); + } + } + + public static class RFC3211DESedeWrap + extends WrapCipherSpi + { + public RFC3211DESedeWrap() + { + super(new RFC3211WrapEngine(new DESedeEngine()), 8); + } + } + */ +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/Arrays.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/Arrays.java new file mode 100644 index 00000000..9600fd53 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/Arrays.java @@ -0,0 +1,214 @@ +package org.bouncycastle.util; + +/** + * General array utilities. + */ +public final class Arrays +{ + private Arrays() + { + // static class, hide constructor + } + + public static boolean areEqual( + boolean[] a, + boolean[] b) + { + if (a == b) + { + return true; + } + + if (a == null || b == null) + { + return false; + } + + if (a.length != b.length) + { + return false; + } + + for (int i = 0; i != a.length; i++) + { + if (a[i] != b[i]) + { + return false; + } + } + + return true; + } + + public static boolean areEqual( + byte[] a, + byte[] b) + { + if (a == b) + { + return true; + } + + if (a == null || b == null) + { + return false; + } + + if (a.length != b.length) + { + return false; + } + + for (int i = 0; i != a.length; i++) + { + if (a[i] != b[i]) + { + return false; + } + } + + return true; + } + + /** + * A constant time equals comparison - does not terminate early if + * test will fail. + * + * @param a first array + * @param b second array + * @return true if arrays equal, false otherwise. + */ + public static boolean constantTimeAreEqual( + byte[] a, + byte[] b) + { + if (a == b) + { + return true; + } + + if (a == null || b == null) + { + return false; + } + + if (a.length != b.length) + { + return false; + } + + int nonEqual = 0; + + for (int i = 0; i != a.length; i++) + { + nonEqual |= (a[i] ^ b[i]); + } + + return nonEqual == 0; + } + + public static boolean areEqual( + int[] a, + int[] b) + { + if (a == b) + { + return true; + } + + if (a == null || b == null) + { + return false; + } + + if (a.length != b.length) + { + return false; + } + + for (int i = 0; i != a.length; i++) + { + if (a[i] != b[i]) + { + return false; + } + } + + return true; + } + + public static void fill( + byte[] array, + byte value) + { + for (int i = 0; i < array.length; i++) + { + array[i] = value; + } + } + + public static void fill( + long[] array, + long value) + { + for (int i = 0; i < array.length; i++) + { + array[i] = value; + } + } + + public static void fill( + short[] array, + short value) + { + for (int i = 0; i < array.length; i++) + { + array[i] = value; + } + } + + public static int hashCode(byte[] data) + { + if (data == null) + { + return 0; + } + + int i = data.length; + int hc = i + 1; + + while (--i >= 0) + { + hc *= 257; + hc ^= data[i]; + } + + return hc; + } + + public static byte[] clone(byte[] data) + { + if (data == null) + { + return null; + } + byte[] copy = new byte[data.length]; + + System.arraycopy(data, 0, copy, 0, data.length); + + return copy; + } + + public static int[] clone(int[] data) + { + if (data == null) + { + return null; + } + int[] copy = new int[data.length]; + + System.arraycopy(data, 0, copy, 0, data.length); + + return copy; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/Strings.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/Strings.java new file mode 100644 index 00000000..253e7226 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/Strings.java @@ -0,0 +1,247 @@ +package org.bouncycastle.util; + +import java.io.ByteArrayOutputStream; +import java.util.Vector; + +public final class Strings +{ + public static String fromUTF8ByteArray(byte[] bytes) + { + int i = 0; + int length = 0; + + while (i < bytes.length) + { + length++; + if ((bytes[i] & 0xf0) == 0xf0) + { + // surrogate pair + length++; + i += 4; + } + else if ((bytes[i] & 0xe0) == 0xe0) + { + i += 3; + } + else if ((bytes[i] & 0xc0) == 0xc0) + { + i += 2; + } + else + { + i += 1; + } + } + + char[] cs = new char[length]; + + i = 0; + length = 0; + + while (i < bytes.length) + { + char ch; + + if ((bytes[i] & 0xf0) == 0xf0) + { + int codePoint = ((bytes[i] & 0x03) << 18) | ((bytes[i+1] & 0x3F) << 12) | ((bytes[i+2] & 0x3F) << 6) | (bytes[i+3] & 0x3F); + int U = codePoint - 0x10000; + char W1 = (char)(0xD800 | (U >> 10)); + char W2 = (char)(0xDC00 | (U & 0x3FF)); + cs[length++] = W1; + ch = W2; + i += 4; + } + else if ((bytes[i] & 0xe0) == 0xe0) + { + ch = (char)(((bytes[i] & 0x0f) << 12) + | ((bytes[i + 1] & 0x3f) << 6) | (bytes[i + 2] & 0x3f)); + i += 3; + } + else if ((bytes[i] & 0xd0) == 0xd0) + { + ch = (char)(((bytes[i] & 0x1f) << 6) | (bytes[i + 1] & 0x3f)); + i += 2; + } + else if ((bytes[i] & 0xc0) == 0xc0) + { + ch = (char)(((bytes[i] & 0x1f) << 6) | (bytes[i + 1] & 0x3f)); + i += 2; + } + else + { + ch = (char)(bytes[i] & 0xff); + i += 1; + } + + cs[length++] = ch; + } + + return new String(cs); + } + + public static byte[] toUTF8ByteArray(String string) + { + return toUTF8ByteArray(string.toCharArray()); + } + + public static byte[] toUTF8ByteArray(char[] string) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + char[] c = string; + int i = 0; + + while (i < c.length) + { + char ch = c[i]; + + if (ch < 0x0080) + { + bOut.write(ch); + } + else if (ch < 0x0800) + { + bOut.write(0xc0 | (ch >> 6)); + bOut.write(0x80 | (ch & 0x3f)); + } + // surrogate pair + else if (ch >= 0xD800 && ch <= 0xDFFF) + { + // in error - can only happen, if the Java String class has a + // bug. + if (i + 1 >= c.length) + { + throw new IllegalStateException("invalid UTF-16 codepoint"); + } + char W1 = ch; + ch = c[++i]; + char W2 = ch; + // in error - can only happen, if the Java String class has a + // bug. + if (W1 > 0xDBFF) + { + throw new IllegalStateException("invalid UTF-16 codepoint"); + } + int codePoint = (((W1 & 0x03FF) << 10) | (W2 & 0x03FF)) + 0x10000; + bOut.write(0xf0 | (codePoint >> 18)); + bOut.write(0x80 | ((codePoint >> 12) & 0x3F)); + bOut.write(0x80 | ((codePoint >> 6) & 0x3F)); + bOut.write(0x80 | (codePoint & 0x3F)); + } + else + { + bOut.write(0xe0 | (ch >> 12)); + bOut.write(0x80 | ((ch >> 6) & 0x3F)); + bOut.write(0x80 | (ch & 0x3F)); + } + + i++; + } + + return bOut.toByteArray(); + } + + /** + * A locale independent version of toUpperCase. + * + * @param string input to be converted + * @return a US Ascii uppercase version + */ + public static String toUpperCase(String string) + { + boolean changed = false; + char[] chars = string.toCharArray(); + + for (int i = 0; i != chars.length; i++) + { + char ch = chars[i]; + if ('a' <= ch && 'z' >= ch) + { + changed = true; + chars[i] = (char)(ch - 'a' + 'A'); + } + } + + if (changed) + { + return new String(chars); + } + + return string; + } + + /** + * A locale independent version of toLowerCase. + * + * @param string input to be converted + * @return a US ASCII lowercase version + */ + public static String toLowerCase(String string) + { + boolean changed = false; + char[] chars = string.toCharArray(); + + for (int i = 0; i != chars.length; i++) + { + char ch = chars[i]; + if ('A' <= ch && 'Z' >= ch) + { + changed = true; + chars[i] = (char)(ch - 'A' + 'a'); + } + } + + if (changed) + { + return new String(chars); + } + + return string; + } + + public static byte[] toByteArray(String string) + { + byte[] bytes = new byte[string.length()]; + + for (int i = 0; i != bytes.length; i++) + { + char ch = string.charAt(i); + + bytes[i] = (byte)ch; + } + + return bytes; + } + + @SuppressWarnings("unchecked") + public static String[] split(String input, char delimiter) + { + Vector v = new Vector(); + boolean moreTokens = true; + String subString; + + while (moreTokens) + { + int tokenLocation = input.indexOf(delimiter); + if (tokenLocation > 0) + { + subString = input.substring(0, tokenLocation); + v.addElement(subString); + input = input.substring(tokenLocation + 1); + } + else + { + moreTokens = false; + v.addElement(input); + } + } + + String[] res = new String[v.size()]; + + for (int i = 0; i != res.length; i++) + { + res[i] = (String)v.elementAt(i); + } + return res; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/encoders/Encoder.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/encoders/Encoder.java new file mode 100644 index 00000000..b0661210 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/encoders/Encoder.java @@ -0,0 +1,17 @@ +package org.bouncycastle.util.encoders; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * Encode and decode byte arrays (typically from binary to 7-bit ASCII + * encodings). + */ +public interface Encoder +{ + int encode(byte[] data, int off, int length, OutputStream out) throws IOException; + + int decode(byte[] data, int off, int length, OutputStream out) throws IOException; + + int decode(String data, OutputStream out) throws IOException; +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/encoders/Hex.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/encoders/Hex.java new file mode 100644 index 00000000..d69f7739 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/encoders/Hex.java @@ -0,0 +1,131 @@ +package org.bouncycastle.util.encoders; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +public class Hex +{ + private static final Encoder encoder = new HexEncoder(); + + /** + * encode the input data producing a Hex encoded byte array. + * + * @return a byte array containing the Hex encoded data. + */ + public static byte[] encode( + byte[] data) + { + return encode(data, 0, data.length); + } + + /** + * encode the input data producing a Hex encoded byte array. + * + * @return a byte array containing the Hex encoded data. + */ + public static byte[] encode( + byte[] data, + int off, + int length) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + try + { + encoder.encode(data, off, length, bOut); + } + catch (IOException e) + { + throw new RuntimeException("exception encoding Hex string: " + e); + } + + return bOut.toByteArray(); + } + + /** + * Hex encode the byte data writing it to the given output stream. + * + * @return the number of bytes produced. + */ + public static int encode( + byte[] data, + OutputStream out) + throws IOException + { + return encoder.encode(data, 0, data.length, out); + } + + /** + * Hex encode the byte data writing it to the given output stream. + * + * @return the number of bytes produced. + */ + public static int encode( + byte[] data, + int off, + int length, + OutputStream out) + throws IOException + { + return encoder.encode(data, off, length, out); + } + + /** + * decode the Hex encoded input data. It is assumed the input data is valid. + * + * @return a byte array representing the decoded data. + */ + public static byte[] decode( + byte[] data) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + try + { + encoder.decode(data, 0, data.length, bOut); + } + catch (IOException e) + { + throw new RuntimeException("exception decoding Hex string: " + e); + } + + return bOut.toByteArray(); + } + + /** + * decode the Hex encoded String data - whitespace will be ignored. + * + * @return a byte array representing the decoded data. + */ + public static byte[] decode( + String data) + { + ByteArrayOutputStream bOut = new ByteArrayOutputStream(); + + try + { + encoder.decode(data, bOut); + } + catch (IOException e) + { + throw new RuntimeException("exception decoding Hex string: " + e); + } + + return bOut.toByteArray(); + } + + /** + * decode the Hex encoded String data writing it to the given output stream, + * whitespace characters will be ignored. + * + * @return the number of bytes produced. + */ + public static int decode( + String data, + OutputStream out) + throws IOException + { + return encoder.decode(data, out); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/encoders/HexEncoder.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/encoders/HexEncoder.java new file mode 100644 index 00000000..0dcae291 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/encoders/HexEncoder.java @@ -0,0 +1,172 @@ +package org.bouncycastle.util.encoders; + +import java.io.IOException; +import java.io.OutputStream; + +public class HexEncoder + implements Encoder +{ + protected final byte[] encodingTable = + { + (byte)'0', (byte)'1', (byte)'2', (byte)'3', (byte)'4', (byte)'5', (byte)'6', (byte)'7', + (byte)'8', (byte)'9', (byte)'a', (byte)'b', (byte)'c', (byte)'d', (byte)'e', (byte)'f' + }; + + /* + * set up the decoding table. + */ + protected final byte[] decodingTable = new byte[128]; + + protected void initialiseDecodingTable() + { + for (int i = 0; i < encodingTable.length; i++) + { + decodingTable[encodingTable[i]] = (byte)i; + } + + decodingTable['A'] = decodingTable['a']; + decodingTable['B'] = decodingTable['b']; + decodingTable['C'] = decodingTable['c']; + decodingTable['D'] = decodingTable['d']; + decodingTable['E'] = decodingTable['e']; + decodingTable['F'] = decodingTable['f']; + } + + public HexEncoder() + { + initialiseDecodingTable(); + } + + /** + * encode the input data producing a Hex output stream. + * + * @return the number of bytes produced. + */ + public int encode( + byte[] data, + int off, + int length, + OutputStream out) + throws IOException + { + for (int i = off; i < (off + length); i++) + { + int v = data[i] & 0xff; + + out.write(encodingTable[(v >>> 4)]); + out.write(encodingTable[v & 0xf]); + } + + return length * 2; + } + + private boolean ignore( + char c) + { + return (c == '\n' || c =='\r' || c == '\t' || c == ' '); + } + + /** + * decode the Hex encoded byte data writing it to the given output stream, + * whitespace characters will be ignored. + * + * @return the number of bytes produced. + */ + public int decode( + byte[] data, + int off, + int length, + OutputStream out) + throws IOException + { + byte b1, b2; + int outLen = 0; + + int end = off + length; + + while (end > off) + { + if (!ignore((char)data[end - 1])) + { + break; + } + + end--; + } + + int i = off; + while (i < end) + { + while (i < end && ignore((char)data[i])) + { + i++; + } + + b1 = decodingTable[data[i++]]; + + while (i < end && ignore((char)data[i])) + { + i++; + } + + b2 = decodingTable[data[i++]]; + + out.write((b1 << 4) | b2); + + outLen++; + } + + return outLen; + } + + /** + * decode the Hex encoded String data writing it to the given output stream, + * whitespace characters will be ignored. + * + * @return the number of bytes produced. + */ + public int decode( + String data, + OutputStream out) + throws IOException + { + byte b1, b2; + int length = 0; + + int end = data.length(); + + while (end > 0) + { + if (!ignore(data.charAt(end - 1))) + { + break; + } + + end--; + } + + int i = 0; + while (i < end) + { + while (i < end && ignore(data.charAt(i))) + { + i++; + } + + b1 = decodingTable[data.charAt(i++)]; + + while (i < end && ignore(data.charAt(i))) + { + i++; + } + + b2 = decodingTable[data.charAt(i++)]; + + out.write((b1 << 4) | b2); + + length++; + } + + return length; + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/io/StreamOverflowException.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/io/StreamOverflowException.java new file mode 100644 index 00000000..1dc49bb9 --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/io/StreamOverflowException.java @@ -0,0 +1,13 @@ +package org.bouncycastle.util.io; + +import java.io.IOException; + +@SuppressWarnings("serial") +public class StreamOverflowException + extends IOException +{ + public StreamOverflowException(String msg) + { + super(msg); + } +} diff --git a/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/io/Streams.java b/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/io/Streams.java new file mode 100644 index 00000000..41560b5d --- /dev/null +++ b/src/java/KP2AKdbLibrary/src/org/bouncycastle/util/io/Streams.java @@ -0,0 +1,87 @@ +package org.bouncycastle.util.io; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public final class Streams +{ + private static int BUFFER_SIZE = 512; + + public static void drain(InputStream inStr) + throws IOException + { + byte[] bs = new byte[BUFFER_SIZE]; + while (inStr.read(bs, 0, bs.length) >= 0) + { + } + } + + public static byte[] readAll(InputStream inStr) + throws IOException + { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + pipeAll(inStr, buf); + return buf.toByteArray(); + } + + public static byte[] readAllLimited(InputStream inStr, int limit) + throws IOException + { + ByteArrayOutputStream buf = new ByteArrayOutputStream(); + pipeAllLimited(inStr, limit, buf); + return buf.toByteArray(); + } + + public static int readFully(InputStream inStr, byte[] buf) + throws IOException + { + return readFully(inStr, buf, 0, buf.length); + } + + public static int readFully(InputStream inStr, byte[] buf, int off, int len) + throws IOException + { + int totalRead = 0; + while (totalRead < len) + { + int numRead = inStr.read(buf, off + totalRead, len - totalRead); + if (numRead < 0) + { + break; + } + totalRead += numRead; + } + return totalRead; + } + + public static void pipeAll(InputStream inStr, OutputStream outStr) + throws IOException + { + byte[] bs = new byte[BUFFER_SIZE]; + int numRead; + while ((numRead = inStr.read(bs, 0, bs.length)) >= 0) + { + outStr.write(bs, 0, numRead); + } + } + + public static long pipeAllLimited(InputStream inStr, long limit, OutputStream outStr) + throws IOException + { + long total = 0; + byte[] bs = new byte[BUFFER_SIZE]; + int numRead; + while ((numRead = inStr.read(bs, 0, bs.length)) >= 0) + { + total += numRead; + if (total > limit) + { + throw new StreamOverflowException("Data Overflow"); + } + outStr.write(bs, 0, numRead); + } + return total; + } +}