Compare commits

...

33 Commits

Author SHA1 Message Date
Robinson 23b9a4f7ff
version 1.48 2023-11-22 22:09:01 +01:00
Robinson bc5c28ef08
Fixed cacheName errors when null. 2023-11-22 12:28:40 +01:00
Robinson 81588e2a8e
Updated build deps 2023-10-09 12:27:09 +02:00
Robinson da9c34b538
updated build deps, kotlin 1.9.0 2023-10-02 16:15:04 +02:00
Robinson efcdbf9559
Version 1.47 2023-09-14 17:54:29 +02:00
Robinson e13205166a
updated unit tests 2023-09-14 17:54:10 +02:00
Robinson 0ce4bb2e2e
Rename .java to .kt 2023-09-14 17:54:10 +02:00
Robinson dc1bfb8371
Cleaned up getTimePretty() output 2023-09-14 17:54:00 +02:00
Robinson 3bf845f355
version 1.46 2023-08-21 01:43:56 +02:00
Robinson 0ff5f377d1
updated license 2023-08-21 01:43:36 +02:00
Robinson 5fa2e2aff6
updated gradle 2023-08-21 01:42:39 +02:00
Robinson db0be638b4
Moved byte array utilities to byteUtils project 2023-08-21 01:41:15 +02:00
Robinson ed5d2d2f74
version 1.45 2023-08-20 11:11:50 +02:00
Robinson ff21f4353e
updated deps 2023-08-20 11:08:30 +02:00
Robinson 0561f66e8e
Added cc0 license 2023-08-07 23:02:41 -06:00
Robinson 869b3b4167
Moved LZMA to byte utils 2023-08-06 17:39:37 -06:00
Robinson a0d02c83fc
code cleanup 2023-08-06 17:22:04 -06:00
Robinson 11c30aed96
Updated classutils 2023-08-05 13:24:48 -06:00
Robinson d3a767806b
Version 1.44 2023-08-05 10:16:25 -06:00
Robinson 27921e22ff
Moved classutils to their own project 2023-08-05 10:15:55 -06:00
Robinson be2dfae824
Updated version 2023-07-12 17:21:29 +02:00
Robinson 9c8f6c97dc
Added mutex.safeUnlock 2023-07-12 17:20:48 +02:00
Robinson f874a0da68
Updated version 2023-07-03 01:27:46 +02:00
Robinson 26a05099e9
updated license 2023-07-03 01:26:46 +02:00
Robinson f2782248db
Code cleanup 2023-07-03 01:26:38 +02:00
Robinson 298e760ee5
Code cleanup 2023-07-03 01:24:50 +02:00
Robinson 52495cbe48
Added version/updates 2023-07-03 01:24:44 +02:00
Robinson b12a86d9bc
Removed fileUtils (can use kotlin library now) 2023-07-03 01:21:02 +02:00
Robinson c4a6c98540
Updated to kotlin 2023-07-03 01:20:37 +02:00
Robinson 61fa96539f
Rename .java to .kt 2023-07-03 01:20:37 +02:00
Robinson 1b5827e33a
Moved all crypto utils to their own project 2023-07-03 00:57:44 +02:00
Robinson c1f1ead3fb
Code cleanup 2023-07-02 11:34:47 +02:00
Robinson 18f7b2a2b8
Moved functions to ByteUtils 2023-07-02 11:34:35 +02:00
74 changed files with 928 additions and 10600 deletions

269
LICENSE
View File

@ -12,23 +12,6 @@
Sean Luke
Michael Lecuyer (portions Copyright 1993
- FileUtil (code from FilenameUtils.java for normalize + dependencies) -
[The Apache Software License, Version 2.0]
https://git.dorkbox.com/dorkbox/Utilities
https://commons.apache.org/proper/commons-io/
Copyright 2013
The Apache Software Foundation
Kevin A. Burton
Scott Sanders
Daniel Rall
Christoph.Reck
Peter Donald
Jeff Turner
Matthew Hawthorne
Martin Cooper
Jeremias Maerki
Stephen Colebourne
- FastThreadLocal -
[BSD 3-Clause License]
https://git.dorkbox.com/dorkbox/Utilities
@ -38,28 +21,6 @@
Lightweight Java Game Library Project
Riven
- Base64Fast -
[BSD 3-Clause License]
https://git.dorkbox.com/dorkbox/Utilities
https://migbase64.sourceforge.net/
Copyright 2004
Mikael Grev, MiG InfoCom AB. (base64@miginfocom.com)
- BCrypt -
[BSD 2-Clause "Simplified" or "FreeBSD" license]
https://git.dorkbox.com/dorkbox/Utilities
https://www.mindrot.org/projects/jBCrypt
Copyright 2006
Damien Miller (djm@mindrot.org)
GWT modified version
- Modified hex conversion utility methods -
[The Apache Software License, Version 2.0]
https://git.dorkbox.com/dorkbox/Utilities
https://netty.io
Copyright 2014
The Netty Project
- Retrofit - A type-safe HTTP client for Android and Java
[The Apache Software License, Version 2.0]
https://github.com/square/retrofit
@ -86,6 +47,12 @@
Copyright 2018
Venkat Peri
- kotlinx.coroutines - Library support for Kotlin coroutines with multiplatform support
[The Apache Software License, Version 2.0]
https://github.com/Kotlin/kotlinx.coroutines
Copyright 2023
JetBrains s.r.o.
- Java Uuid Generator - A set of Java classes for working with UUIDs
[The Apache Software License, Version 2.0]
https://github.com/cowtowncoder/java-uuid-generator
@ -93,44 +60,6 @@
Tatu Saloranta (tatu.saloranta@iki.fi)
Contributors. See source release-notes/CREDITS
- SLF4J - Simple facade or abstraction for various logging frameworks
[MIT License]
https://www.slf4j.org
Copyright 2023
QOS.ch
- XZ for Java - Complete implementation of XZ data compression in pure Java
[Public Domain, per Creative Commons CC0]
https://tukaani.org/xz/java.html
Copyright 2023
Lasse Collin
Igor Pavlov
- Netty - An event-driven asynchronous network application framework
[The Apache Software License, Version 2.0]
https://netty.io
Copyright 2023
The Netty Project
Contributors. See source NOTICE
- Bouncy Castle Crypto - Lightweight cryptography API and JCE Extension
[The Apache Software License, Version 2.0]
https://www.bouncycastle.org
Copyright 2023
The Legion of the Bouncy Castle Inc
- Lightweight Java Game Library - Java library that enables cross-platform access to popular native APIs
[BSD 3-Clause License]
https://github.com/LWJGL/lwjgl3
Copyright 2023
Lightweight Java Game Library
- TypeTools - A simple, zero-dependency library for working with types. Supports Java 1.6+ and Android.
[The Apache Software License, Version 2.0]
https://github.com/jhalterman/typetools
Copyright 2023
Jonathan Halterman and friends
- Kotlin -
[The Apache Software License, Version 2.0]
https://github.com/JetBrains/kotlin
@ -139,192 +68,6 @@
Kotlin Compiler, Test Data+Libraries, and Tools repository contain third-party code, to which different licenses may apply
See: https://github.com/JetBrains/kotlin/blob/master/license/README.md
- kotlinx.coroutines - Library support for Kotlin coroutines with multiplatform support
[The Apache Software License, Version 2.0]
https://github.com/Kotlin/kotlinx.coroutines
Copyright 2023
JetBrains s.r.o.
- Collections - Niche collections to augment what is already available.
[The Apache Software License, Version 2.0]
https://git.dorkbox.com/dorkbox/Collections
Copyright 2023
Dorkbox LLC
Extra license information
- Bias, BinarySearch -
[MIT License]
https://git.dorkbox.com/dorkbox/Collections
https://github.com/timboudreau/util
Copyright 2013
Tim Boudreau
- ConcurrentEntry -
[The Apache Software License, Version 2.0]
https://git.dorkbox.com/dorkbox/Collections
Copyright 2016
bennidi
dorkbox
- Collection Utilities (Array, ArrayMap, BooleanArray, ByteArray, CharArray, FloatArray, IdentityMap, IntArray, IntFloatMap, IntIntMap, IntMap, IntSet, LongArray, LongMap, ObjectFloatMap, ObjectIntMap, ObjectMap, ObjectSet, OrderedMap, OrderedSet) -
[The Apache Software License, Version 2.0]
https://git.dorkbox.com/dorkbox/Collections
https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/utils
Copyright 2011
LibGDX
Mario Zechner (badlogicgames@gmail.com)
Nathan Sweet (nathan.sweet@gmail.com)
- Predicate -
[The Apache Software License, Version 2.0]
https://git.dorkbox.com/dorkbox/Collections
https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/utils
Copyright 2011
LibGDX
Mario Zechner (badlogicgames@gmail.com)
Nathan Sweet (nathan.sweet@gmail.com)
xoppa
- Select, QuickSelect -
[The Apache Software License, Version 2.0]
https://git.dorkbox.com/dorkbox/Collections
https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/utils
Copyright 2011
LibGDX
Mario Zechner (badlogicgames@gmail.com)
Nathan Sweet (nathan.sweet@gmail.com)
Jon Renner
- TimSort, ComparableTimSort -
[The Apache Software License, Version 2.0]
https://git.dorkbox.com/dorkbox/Collections
https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/utils
Copyright 2008
The Android Open Source Project
- ConcurrentWeakIdentityHashMap - Concurrent WeakIdentity HashMap
[The Apache Software License, Version 2.0]
https://github.com/spring-projects/spring-loaded/blob/master/springloaded/src/main/java/org/springsource/loaded/support/ConcurrentWeakIdentityHashMap.java
Copyright 2016
zhanhb
- Kotlin -
[The Apache Software License, Version 2.0]
https://github.com/JetBrains/kotlin
Copyright 2020
JetBrains s.r.o. and Kotlin Programming Language contributors
Kotlin Compiler, Test Data+Libraries, and Tools repository contain third-party code, to which different licenses may apply
See: https://github.com/JetBrains/kotlin/blob/master/license/README.md
- Updates - Software Update Management
[The Apache Software License, Version 2.0]
https://git.dorkbox.com/dorkbox/Updates
Copyright 2021
Dorkbox LLC
Extra license information
- Kotlin -
[The Apache Software License, Version 2.0]
https://github.com/JetBrains/kotlin
Copyright 2020
JetBrains s.r.o. and Kotlin Programming Language contributors
Kotlin Compiler, Test Data+Libraries, and Tools repository contain third-party code, to which different licenses may apply
See: https://github.com/JetBrains/kotlin/blob/master/license/README.md
- Executor - Shell, JVM, and SSH command execution on Linux, MacOS, or Windows for Java 8+
[The Apache Software License, Version 2.0]
https://git.dorkbox.com/dorkbox/Executor
Copyright 2023
Dorkbox LLC
Extra license information
- ZT Process Executor -
[The Apache Software License, Version 2.0]
https://github.com/zeroturnaround/zt-exec
Copyright 2014
ZeroTurnaround LLC
- Apache Commons Exec -
[The Apache Software License, Version 2.0]
https://commons.apache.org/proper/commons-exec/
Copyright 2014
The Apache Software Foundation
- Kotlin -
[The Apache Software License, Version 2.0]
https://github.com/JetBrains/kotlin
Copyright 2020
JetBrains s.r.o. and Kotlin Programming Language contributors
Kotlin Compiler, Test Data+Libraries, and Tools repository contain third-party code, to which different licenses may apply
See: https://github.com/JetBrains/kotlin/blob/master/license/README.md
- kotlinx.coroutines - Library support for Kotlin coroutines with multiplatform support
[The Apache Software License, Version 2.0]
https://github.com/Kotlin/kotlinx.coroutines
Copyright 2023
JetBrains s.r.o.
- SLF4J - Simple facade or abstraction for various logging frameworks
[MIT License]
https://www.slf4j.org
Copyright 2023
QOS.ch
- Logback - Logback is a logging framework for Java applications
[The Apache Software License, Version 2.0]
https://logback.qos.ch
Copyright 2023
QOS.ch
- SSHJ - SSHv2 library for Java
[The Apache Software License, Version 2.0]
https://github.com/hierynomus/sshj
Copyright 2023
Jeroen van Erp
SSHJ Contributors
Extra license information
- Apache MINA -
[The Apache Software License, Version 2.0]
https://mina.apache.org/sshd-project/
The Apache Software Foundation
- Apache Commons-Net -
[The Apache Software License, Version 2.0]
https://commons.apache.org/proper/commons-net/
The Apache Software Foundation
- JZlib -
[The Apache Software License, Version 2.0]
https://github.com/ymnk/jzlib
Atsuhiko Yamanaka
JCraft, Inc.
- Bouncy Castle Crypto -
[The Apache Software License, Version 2.0]
https://www.bouncycastle.org
The Legion of the Bouncy Castle Inc
- ed25519-java -
[Public Domain, per Creative Commons CC0]
https://github.com/str4d/ed25519-java
https://github.com/str4d
- Updates - Software Update Management
[The Apache Software License, Version 2.0]
https://git.dorkbox.com/dorkbox/Updates
Copyright 2021
Dorkbox LLC
Extra license information
- Kotlin -
[The Apache Software License, Version 2.0]
https://github.com/JetBrains/kotlin
Copyright 2020
JetBrains s.r.o. and Kotlin Programming Language contributors
Kotlin Compiler, Test Data+Libraries, and Tools repository contain third-party code, to which different licenses may apply
See: https://github.com/JetBrains/kotlin/blob/master/license/README.md
- OS - Information about the system, Java runtime, OS, Window Manager, and Desktop Environment.
[The Apache Software License, Version 2.0]
https://git.dorkbox.com/dorkbox/OS

View File

@ -1,22 +0,0 @@
BSD License
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <ORGANIZATION> 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.

View File

@ -1,121 +0,0 @@
Creative Commons Legal Code
CC0 1.0 Universal
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
HEREUNDER.
Statement of Purpose
The laws of most jurisdictions throughout the world automatically confer
exclusive Copyright and Related Rights (defined below) upon the creator
and subsequent owner(s) (each and all, an "owner") of an original work of
authorship and/or a database (each, a "Work").
Certain owners wish to permanently relinquish those rights to a Work for
the purpose of contributing to a commons of creative, cultural and
scientific works ("Commons") that the public can reliably and without fear
of later claims of infringement build upon, modify, incorporate in other
works, reuse and redistribute as freely as possible in any form whatsoever
and for any purposes, including without limitation commercial purposes.
These owners may contribute to the Commons to promote the ideal of a free
culture and the further production of creative, cultural and scientific
works, or to gain reputation or greater distribution for their Work in
part through the use and efforts of others.
For these and/or other purposes and motivations, and without any
expectation of additional consideration or compensation, the person
associating CC0 with a Work (the "Affirmer"), to the extent that he or she
is an owner of Copyright and Related Rights in the Work, voluntarily
elects to apply CC0 to the Work and publicly distribute the Work under its
terms, with knowledge of his or her Copyright and Related Rights in the
Work and the meaning and intended legal effect of CC0 on those rights.
1. Copyright and Related Rights. A Work made available under CC0 may be
protected by copyright and related or neighboring rights ("Copyright and
Related Rights"). Copyright and Related Rights include, but are not
limited to, the following:
i. the right to reproduce, adapt, distribute, perform, display,
communicate, and translate a Work;
ii. moral rights retained by the original author(s) and/or performer(s);
iii. publicity and privacy rights pertaining to a person's image or
likeness depicted in a Work;
iv. rights protecting against unfair competition in regards to a Work,
subject to the limitations in paragraph 4(a), below;
v. rights protecting the extraction, dissemination, use and reuse of data
in a Work;
vi. database rights (such as those arising under Directive 96/9/EC of the
European Parliament and of the Council of 11 March 1996 on the legal
protection of databases, and under any national implementation
thereof, including any amended or successor version of such
directive); and
vii. other similar, equivalent or corresponding rights throughout the
world based on applicable law or treaty, and any national
implementations thereof.
2. Waiver. To the greatest extent permitted by, but not in contravention
of, applicable law, Affirmer hereby overtly, fully, permanently,
irrevocably and unconditionally waives, abandons, and surrenders all of
Affirmer's Copyright and Related Rights and associated claims and causes
of action, whether now known or unknown (including existing as well as
future claims and causes of action), in the Work (i) in all territories
worldwide, (ii) for the maximum duration provided by applicable law or
treaty (including future time extensions), (iii) in any current or future
medium and for any number of copies, and (iv) for any purpose whatsoever,
including without limitation commercial, advertising or promotional
purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
member of the public at large and to the detriment of Affirmer's heirs and
successors, fully intending that such Waiver shall not be subject to
revocation, rescission, cancellation, termination, or any other legal or
equitable action to disrupt the quiet enjoyment of the Work by the public
as contemplated by Affirmer's express Statement of Purpose.
3. Public License Fallback. Should any part of the Waiver for any reason
be judged legally invalid or ineffective under applicable law, then the
Waiver shall be preserved to the maximum extent permitted taking into
account Affirmer's express Statement of Purpose. In addition, to the
extent the Waiver is so judged Affirmer hereby grants to each affected
person a royalty-free, non transferable, non sublicensable, non exclusive,
irrevocable and unconditional license to exercise Affirmer's Copyright and
Related Rights in the Work (i) in all territories worldwide, (ii) for the
maximum duration provided by applicable law or treaty (including future
time extensions), (iii) in any current or future medium and for any number
of copies, and (iv) for any purpose whatsoever, including without
limitation commercial, advertising or promotional purposes (the
"License"). The License shall be deemed effective as of the date CC0 was
applied by Affirmer to the Work. Should any part of the License for any
reason be judged legally invalid or ineffective under applicable law, such
partial invalidity or ineffectiveness shall not invalidate the remainder
of the License, and in such case Affirmer hereby affirms that he or she
will not (i) exercise any of his or her remaining Copyright and Related
Rights in the Work or (ii) assert any associated claims and causes of
action with respect to the Work, in either case contrary to Affirmer's
express Statement of Purpose.
4. Limitations and Disclaimers.
a. No trademark or patent rights held by Affirmer are waived, abandoned,
surrendered, licensed or otherwise affected by this document.
b. Affirmer offers the Work as-is and makes no representations or
warranties of any kind concerning the Work, express, implied,
statutory or otherwise, including without limitation warranties of
title, merchantability, fitness for a particular purpose, non
infringement, or the absence of latent or other defects, accuracy, or
the present or absence of errors, whether or not discoverable, all to
the greatest extent permissible under applicable law.
c. Affirmer disclaims responsibility for clearing rights of other persons
that may apply to the Work or any use thereof, including without
limitation any person's Copyright and Related Rights in the Work.
Further, Affirmer disclaims responsibility for obtaining any necessary
consents, permissions or other rights required for any use of the
Work.
d. Affirmer understands and acknowledges that Creative Commons is not a
party to this document and has no duty or obligation with respect to
this CC0 or use of the Work.

View File

@ -1,21 +0,0 @@
MIT License
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.

View File

@ -11,25 +11,25 @@ Please see the header of each file for the specific license that applies to it.
Maven Info
---------
````
```
<dependencies>
...
<dependency>
<groupId>com.dorkbox</groupId>
<artifactId>Utilities</artifactId>
<version>1.36</version>
<version>1.48</version>
</dependency>
</dependencies>
````
```
Gradle Info
---------
````
```
dependencies {
...
compile "com.dorkbox:Utilities:1.36"
compile "com.dorkbox:Utilities:1.48"
}
````
```
License

View File

@ -23,19 +23,19 @@
gradle.startParameter.showStacktrace = ShowStacktrace.ALWAYS // always show the stacktrace!
plugins {
id("com.dorkbox.GradleUtils") version "3.17"
id("com.dorkbox.Licensing") version "2.24"
id("com.dorkbox.GradleUtils") version "3.18"
id("com.dorkbox.Licensing") version "2.28"
id("com.dorkbox.VersionUpdate") version "2.8"
id("com.dorkbox.GradlePublish") version "1.18"
id("com.dorkbox.GradlePublish") version "1.20"
kotlin("jvm") version "1.8.0"
kotlin("jvm") version "1.9.0"
}
object Extras {
// set for the project
const val description = "Utilities for use within Java projects"
const val group = "com.dorkbox"
const val version = "1.41"
const val version = "1.48"
// set as project.ext
const val name = "Utilities"
@ -66,22 +66,6 @@ licensing {
author("Sean Luke")
author("Michael Lecuyer (portions Copyright 1993")
}
extra("FileUtil (code from FilenameUtils.java for normalize + dependencies)", License.APACHE_2) {
url(Extras.url)
url("https://commons.apache.org/proper/commons-io/")
copyright(2013)
author("The Apache Software Foundation")
author("Kevin A. Burton")
author("Scott Sanders")
author("Daniel Rall")
author("Christoph.Reck")
author("Peter Donald")
author("Jeff Turner")
author("Matthew Hawthorne")
author("Martin Cooper")
author("Jeremias Maerki")
author("Stephen Colebourne")
}
extra("FastThreadLocal", License.BSD_3) {
url(Extras.url)
url("https://github.com/LWJGL/lwjgl3/blob/5819c9123222f6ce51f208e022cb907091dd8023/modules/core/src/main/java/org/lwjgl/system/FastThreadLocal.java")
@ -90,25 +74,7 @@ licensing {
author("Lightweight Java Game Library Project")
author("Riven")
}
extra("Base64Fast", License.BSD_3) {
url(Extras.url)
url("https://migbase64.sourceforge.net/")
copyright(2004)
author("Mikael Grev, MiG InfoCom AB. (base64@miginfocom.com)")
}
extra("BCrypt", License.BSD_2) {
url(Extras.url)
url("https://www.mindrot.org/projects/jBCrypt")
copyright(2006)
author("Damien Miller (djm@mindrot.org)")
note("GWT modified version")
}
extra("Modified hex conversion utility methods", License.APACHE_2) {
url(Extras.url)
url("https://netty.io")
copyright(2014)
author("The Netty Project")
}
extra("Retrofit", License.APACHE_2) {
copyright(2020)
description("A type-safe HTTP client for Android and Java")
@ -155,11 +121,9 @@ tasks.jar.get().apply {
// NOTE: compileOnly is used because there are some classes/dependencies that ARE NOT necessary to be included, UNLESS the user
// is actually using that part of the library. If this happens, they will (or should) already be using the dependency)
dependencies {
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
api("com.dorkbox:Collections:1.6")
api("com.dorkbox:Executor:3.13")
api("com.dorkbox:OS:1.6")
api("com.dorkbox:OS:1.8")
api("com.dorkbox:Updates:1.1")
@ -169,46 +133,19 @@ dependencies {
// // https://github.com/MicroUtils/kotlin-logging NO JPMS SUPPORT!
// api("io.github.microutils:kotlin-logging:3.0.4")
api("org.slf4j:slf4j-api:2.0.7")
// api("org.slf4j:slf4j-api:2.0.7")
api("org.tukaani:xz:1.9")
compileOnly("com.fasterxml.uuid:java-uuid-generator:4.1.0")
// api "com.koloboke:koloboke-api-jdk8:1.0.0"
// runtime "com.koloboke:koloboke-impl-jdk8:1.0.0"
// compileOnly("com.esotericsoftware:kryo:5.3.0")
// compileOnly("de.javakaffee:kryo-serializers:0.45")
compileOnly("io.netty:netty-buffer:4.1.94.Final")
val bcVersion = "1.70"
compileOnly("org.bouncycastle:bcprov-jdk15on:$bcVersion")
compileOnly("org.bouncycastle:bcpg-jdk15on:$bcVersion")
compileOnly("org.bouncycastle:bcmail-jdk15on:$bcVersion")
compileOnly("org.bouncycastle:bctls-jdk15on:$bcVersion")
compileOnly("org.lwjgl:lwjgl-xxhash:3.3.2")
compileOnly("net.jodah:typetools:0.6.3")
// compileOnly("io.netty:netty-buffer:4.1.96.Final")
// testing
testImplementation("org.bouncycastle:bcprov-jdk15on:$bcVersion")
testImplementation("org.bouncycastle:bcpg-jdk15on:$bcVersion")
testImplementation("org.bouncycastle:bcmail-jdk15on:$bcVersion")
testImplementation("org.bouncycastle:bctls-jdk15on:$bcVersion")
testImplementation("com.esotericsoftware:kryo:5.4.0")
testImplementation("de.javakaffee:kryo-serializers:0.45")
testImplementation("com.dorkbox:Serializers:2.7")
testImplementation("com.dorkbox:Executor:3.13")
testImplementation("junit:junit:4.13.2")
testImplementation("ch.qos.logback:logback-classic:1.4.5")
}
repositories {
mavenCentral()
// testImplementation("ch.qos.logback:logback-classic:1.4.5")
// implementation(kotlin("stdlib-jdk8"))
}
publishToSonatype {

Binary file not shown.

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

12
gradlew vendored Normal file → Executable file
View File

@ -55,7 +55,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@ -80,10 +80,10 @@ do
esac
done
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
APP_NAME="Gradle"
# This is normally unused
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
@ -143,12 +143,16 @@ fi
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
case $MAX_FD in #(
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac

1
gradlew.bat vendored
View File

@ -26,6 +26,7 @@ if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%"=="" set DIRNAME=.
@rem This is normally unused
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

View File

@ -1,5 +1,5 @@
/*
* Copyright 2018 dorkbox, llc
* Copyright 2023 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -13,3 +13,4 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
rootProject.name = "Utilities"

View File

@ -18,7 +18,6 @@ package dorkbox.util
import dorkbox.os.OS.TEMP_DIR
import dorkbox.util.FileUtil.copyFile
import dorkbox.util.FileUtil.delete
import dorkbox.util.FileUtil.getExtension
import java.io.*
import java.math.BigInteger
import java.net.URL
@ -31,7 +30,7 @@ class CacheUtil(private val tempDir: String = "cache") {
* Clears ALL saved files in the cache
*/
fun clear() {
// deletes all of the files (recursively) in the specified location. If the directory is empty (no locked files), then the
// deletes all the files (recursively) in the specified location. If the directory is empty (no locked files), then the
// directory is also deleted.
delete(File(TEMP_DIR, tempDir))
}
@ -71,10 +70,7 @@ class CacheUtil(private val tempDir: String = "cache") {
/**
* Checks to see if the specified URL is in the cache. NULL if it is not, otherwise specifies a location on disk.
*/
fun check(fileResource: URL?): File? {
if (fileResource == null) {
throw NullPointerException("fileResource")
}
fun check(fileResource: URL): File? {
return check(fileResource.path)
}
@ -83,10 +79,7 @@ class CacheUtil(private val tempDir: String = "cache") {
* specifies a location on disk.
*/
@Throws(IOException::class)
fun check(fileStream: InputStream?): File? {
if (fileStream == null) {
throw NullPointerException("fileStream")
}
fun check(fileStream: InputStream): File? {
return check(null, fileStream)
}
@ -95,17 +88,13 @@ class CacheUtil(private val tempDir: String = "cache") {
* cacheName is NULL, it will use a HASH of the fileStream
*/
@Throws(IOException::class)
fun check(cacheName: String?, fileStream: InputStream?): File? {
var cacheName = cacheName
if (fileStream == null) {
throw NullPointerException("fileStream")
}
if (cacheName == null) {
cacheName = createNameAsHash(fileStream)
}
fun check(cacheName: String?, fileStream: InputStream): File? {
// if we already have this fileName, reuse it
val newFile = makeCacheFile(cacheName)
val newFile = if (cacheName == null) {
makeCacheFile(createNameAsHash(fileStream))
} else {
makeCacheFile(cacheName)
}
// if this file already exists (via HASH), we just reuse what is saved on disk.
return if (newFile.canRead() && newFile.isFile) {
@ -126,11 +115,11 @@ class CacheUtil(private val tempDir: String = "cache") {
*/
@Throws(IOException::class)
fun save(cacheName: String?, file: File): File {
var cacheName = cacheName
if (cacheName == null) {
cacheName = file.absolutePath
return if (cacheName == null) {
save(file.absolutePath, file.absolutePath)
} else {
save(cacheName, file.absolutePath)
}
return save(cacheName, file.absolutePath)
}
/**
@ -148,12 +137,10 @@ class CacheUtil(private val tempDir: String = "cache") {
*/
@Throws(IOException::class)
fun save(cacheName: String?, fileName: String): File {
var cacheName = cacheName
if (cacheName == null) {
cacheName = fileName
}
// if we already have this fileName, reuse it
@Suppress("NAME_SHADOWING")
val cacheName = cacheName ?: fileName
val newFile = makeCacheFile(cacheName)
// if this file already exists (via HASH), we just reuse what is saved on disk.
@ -195,12 +182,10 @@ class CacheUtil(private val tempDir: String = "cache") {
*/
@Throws(IOException::class)
fun save(cacheName: String?, fileResource: URL): File {
var cacheName = cacheName
if (cacheName == null) {
cacheName = fileResource.path
}
// if we already have this fileName, reuse it
@Suppress("NAME_SHADOWING")
val cacheName = cacheName ?: fileResource.path
val newFile = makeCacheFile(cacheName)
// if this file already exists (via HASH), we just reuse what is saved on disk.
@ -230,12 +215,10 @@ class CacheUtil(private val tempDir: String = "cache") {
*/
@Throws(IOException::class)
fun save(cacheName: String?, fileStream: InputStream): File {
var cacheName = cacheName
if (cacheName == null) {
cacheName = createNameAsHash(fileStream)
}
// if we already have this fileName, reuse it
@Suppress("NAME_SHADOWING")
val cacheName = cacheName ?: createNameAsHash(fileStream)
val newFile = makeCacheFile(cacheName)
// if this file already exists (via HASH), we just reuse what is saved on disk.
@ -253,13 +236,7 @@ class CacheUtil(private val tempDir: String = "cache") {
* @return the full path of the resource copied to disk, or NULL if invalid
*/
@Throws(IOException::class)
private fun makeFileViaStream(cacheName: String?, resourceStream: InputStream?): File {
if (resourceStream == null) {
throw NullPointerException("resourceStream")
}
if (cacheName == null) {
throw NullPointerException("cacheName")
}
private fun makeFileViaStream(cacheName: String, resourceStream: InputStream): File {
val newFile = makeCacheFile(cacheName)
// if this file already exists (via HASH), we just reuse what is saved on disk.
@ -298,22 +275,19 @@ class CacheUtil(private val tempDir: String = "cache") {
*
* @return the file on disk represented by the file name
*/
fun create(cacheName: String?): File {
fun create(cacheName: String): File {
return makeCacheFile(cacheName)
}
// creates the file that will be cached. It may, or may not already exist
// must be called from synchronized block!
// never returns null
private fun makeCacheFile(cacheName: String?): File {
if (cacheName == null) {
throw NullPointerException("cacheName")
}
private fun makeCacheFile(cacheName: String): File {
val saveDir = File(TEMP_DIR, tempDir)
// can be wimpy, only one at a time
val hash = hashName(cacheName)
var extension = getExtension(cacheName)
var extension = Sys.getExtension(cacheName)
if (extension.isEmpty()) {
extension = "cache"
}
@ -324,6 +298,11 @@ class CacheUtil(private val tempDir: String = "cache") {
}
companion object {
/**
* Gets the version number.
*/
val version = Sys.version
private val digestLocal = ThreadLocal.withInitial {
try {
return@withInitial MessageDigest.getInstance("SHA1")

View File

@ -66,6 +66,12 @@ inline fun ignoreExceptions(vararg blocks: () -> Unit) {
}
}
fun Mutex.safeUnlock() {
if (isLocked) {
unlock()
}
}
// From: https://elizarov.medium.com/phantom-of-the-coroutine-afc63b03a131
suspend inline fun <T> Mutex.withReentrantLock(crossinline block: suspend () -> T): T {
val key = ReentrantMutexContextKey(this)

View File

@ -1,115 +0,0 @@
/*
* Copyright 2010 dorkbox, llc
*
* 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.
*/
package dorkbox.util;
import java.util.Timer;
import java.util.TimerTask;
public
class DelayTimer {
private final String name;
private final boolean isDaemon;
private final Runnable listener;
private volatile Timer timer;
private long delay;
public
DelayTimer(Runnable listener) {
this(null, true, listener);
}
/**
* Sometimes you want to make sure that this timer will complete, even if the calling thread has terminated.
*
* @param name the name of the thread (if you want to specify one)
* @param isDaemon true if you want this timer to be run on a daemon thread
* @param listener the callback listener to execute
*/
public
DelayTimer(String name, boolean isDaemon, Runnable listener) {
this.name = name;
this.listener = listener;
this.isDaemon = isDaemon;
}
/**
* @return true if this timer is still waiting to run.
*/
public synchronized
boolean isWaiting() {
return this.timer != null;
}
/**
* Cancel the delay timer!
*/
public synchronized
void cancel() {
if (this.timer != null) {
this.timer.cancel();
this.timer.purge();
this.timer = null;
}
}
/**
* @param delay milliseconds to wait
*/
public synchronized
void delay(long delay) {
this.delay = delay;
cancel();
if (delay > 0) {
if (this.name != null) {
this.timer = new Timer(this.name, this.isDaemon);
}
else {
this.timer = new Timer(this.isDaemon);
}
TimerTask t = new TimerTask() {
@Override
public
void run() {
// timer can change if the callback calls delay() or cancel()
Timer origTimer = DelayTimer.this.timer;
DelayTimer.this.listener.run();
if (origTimer != null) {
origTimer.cancel();
origTimer.purge();
if (origTimer == DelayTimer.this.timer) {
DelayTimer.this.timer = null;
}
}
}
};
this.timer.schedule(t, delay);
}
else {
this.listener.run();
this.timer = null;
}
}
public synchronized
long getDelay() {
return this.delay;
}
}

View File

@ -0,0 +1,102 @@
/*
* Copyright 2023 dorkbox, llc
*
* 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.
*/
package dorkbox.util
import java.util.*
/**
* Sometimes you want to make sure that this timer will complete, even if the calling thread has terminated.
*/
class DelayTimer(
/**
* the name of the thread (if you want to specify one)
*/
private val name: String,
/**
* true if you want this timer to be run on a daemon thread
*/
private val isDaemon: Boolean,
/**
* the callback listener to execute
*/
private val listener: Runnable) {
companion object {
/**
* Gets the version number.
*/
val version = Sys.version
}
@Volatile
private var timer: Timer? = null
@get:Synchronized
var delay: Long = 0
private set
constructor(listener: Runnable) : this("Timer-", true, listener)
@get:Synchronized
val isWaiting: Boolean
/**
* @return true if this timer is still waiting to run.
*/
get() = timer != null
/**
* Cancel the delay timer!
*/
@Synchronized
fun cancel() {
if (timer != null) {
timer!!.cancel()
timer!!.purge()
timer = null
}
}
/**
* @param delay milliseconds to wait
*/
@Synchronized
fun delay(delay: Long) {
this.delay = delay
cancel()
if (delay > 0) {
timer = Timer(name, isDaemon)
val t: TimerTask = object : TimerTask() {
override fun run() {
// timer can change if the callback calls delay() or cancel()
val origTimer = timer
listener.run()
if (origTimer != null) {
origTimer.cancel()
origTimer.purge()
if (origTimer === timer) {
timer = null
}
}
}
}
timer!!.schedule(t, delay)
} else {
listener.run()
timer = null
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010 dorkbox, llc
* Copyright 2023 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,20 +18,7 @@ package dorkbox.util
import dorkbox.os.OS
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import java.io.BufferedInputStream
import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.File
import java.io.FileInputStream
import java.io.FileNotFoundException
import java.io.FileOutputStream
import java.io.FileReader
import java.io.FileWriter
import java.io.IOException
import java.io.InputStream
import java.io.PrintWriter
import java.io.RandomAccessFile
import java.io.Reader
import java.io.*
import java.nio.file.DirectoryIteratorException
import java.util.*
import java.util.zip.*
@ -57,7 +44,7 @@ object FileUtil {
/**
* Gets the version number.
*/
val version = "1.41"
val version = Sys.version
private val log: Logger = LoggerFactory.getLogger(FileUtil::class.java)
@ -569,12 +556,11 @@ object FileUtil {
/**
* Copies a files from one location to another. Overwriting any existing file at the destination.
*/
@JvmStatic
@Throws(IOException::class)
fun copyFile(`in`: File, out: File): File {
val normalizedIn = normalize(`in`)!!.absolutePath
val normalizedout = normalize(out)!!.absolutePath
if (normalizedIn.equals(normalizedout, ignoreCase = true)) {
val normalizedIn = `in`.normalize().absolutePath
val normalizedOut = out.normalize().absolutePath
if (normalizedIn.equals(normalizedOut, ignoreCase = true)) {
if (DEBUG) {
System.err.println("Source equals destination! $normalizedIn")
}
@ -662,8 +648,8 @@ object FileUtil {
*/
@Throws(IOException::class)
fun copyDirectory(src_: File, dest_: File, vararg namesToIgnore: String) {
val src = normalize(src_)
val dest = normalize(dest_)
val src = src_.normalize()
val dest = dest_.normalize()
requireNotNull(src) { "Source must be valid" }
requireNotNull(dest) { "Destination must be valid" }
@ -769,7 +755,6 @@ object FileUtil {
*
* @return true IFF the file/dir was deleted or didn't exist at first
*/
@JvmStatic
fun delete(file: File, vararg namesToIgnore: String): Boolean {
if (!file.exists()) {
return true
@ -785,7 +770,7 @@ object FileUtil {
var delete = true
val file2 = files[i]
val name2 = file2.name
val name2Full = normalize(file2)!!.absolutePath
val name2Full = file2.normalize().absolutePath
if (file2.isDirectory) {
for (name in namesToIgnore) {
if (name[0] == UNIX_SEPARATOR && name == name2) {
@ -861,7 +846,7 @@ object FileUtil {
/**
* @return the contents of the file as a byte array
*/
fun toBytes(file: File): ByteArray? {
fun toBytes(file: File): ByteArray {
return file.readBytes()
}
@ -869,7 +854,7 @@ object FileUtil {
* Creates the directories in the specified location.
*/
fun mkdir(location: File): String {
val path = normalize(location)!!.absoluteFile
val path = location.normalize().absoluteFile
if (location.mkdirs()) {
if (DEBUG) {
System.err.println("Created directory: $path")
@ -890,7 +875,7 @@ object FileUtil {
*/
@Throws(IOException::class)
fun tempFile(fileName: String): File {
return normalize(File.createTempFile(fileName, null))!!.absoluteFile
return File.createTempFile(fileName, null).normalize().absoluteFile
}
/**
@ -905,7 +890,7 @@ object FileUtil {
if (!file.mkdir()) {
throw IOException("Unable to create temp directory: $file")
}
return normalize(file)!!.absolutePath
return file.normalize().absolutePath
}
/**
@ -1045,16 +1030,15 @@ object FileUtil {
*/
@Throws(IOException::class)
private fun unjarzip1(inputStream: ZipInputStream, outputDir: File, extractManifest: Boolean) {
inputStream.use {
var entry: ZipEntry
var entry: ZipEntry?
while (inputStream.nextEntry.also { entry = it } != null) {
val name = entry.name
val name = entry!!.name
if (!extractManifest && name.startsWith("META-INF/")) {
continue
}
val file = File(outputDir, name)
if (entry.isDirectory) {
if (entry!!.isDirectory) {
mkdir(file.path)
continue
}
@ -1068,7 +1052,7 @@ object FileUtil {
}
/**
* Parses the specified root directory for **ALL** files that are in it. All of the sub-directories are searched as well.
* Parses the specified root directory for **ALL** files that are in it. All the sub-directories are searched as well.
*
*
* *This is different, in that it returns ALL FILES, instead of ones that just match a specific extension.*
@ -1081,7 +1065,7 @@ object FileUtil {
}
/**
* Parses the specified root directory for **ALL** files that are in it. All of the sub-directories are searched as well.
* Parses the specified root directory for **ALL** files that are in it. All the sub-directories are searched as well.
*
*
* *This is different, in that it returns ALL FILES, instead of ones that just match a specific extension.*
@ -1094,7 +1078,7 @@ object FileUtil {
}
/**
* Parses the specified root directory for files that end in the extension to match. All of the sub-directories are searched as well.
* Parses the specified root directory for files that end in the extension to match. All the sub-directories are searched as well.
*
* @return the list of all files in the root+sub-dirs that match the given extension.
*/
@ -1104,7 +1088,7 @@ object FileUtil {
val directories = LinkedList<File?>()
@Suppress("NAME_SHADOWING")
val rootDirectory = normalize(rootDirectory) ?: throw IOException("Root directory was invalid!")
val rootDirectory = rootDirectory.normalize()
if (!rootDirectory.exists()) {
throw IOException("Location does not exist: " + rootDirectory.absolutePath)
@ -1348,625 +1332,4 @@ object FileUtil {
}
return file.setLastModified(timestamp)
}
//-----------------------------------------------------------------------
/*
* FilenameUtils.java (normalize + dependencies) - Apache 2.0 License
* http://commons.apache.org/proper/commons-io/
* Copyright 2013 ASF
* Authors: Kevin A. Burton, Scott Sanders, Daniel Rall, Christoph.Reck,
* Peter Donald, Jeff Turner, Matthew Hawthorne, Martin Cooper,
* Jeremias Maerki, Stephen Colebourne
*/
/**
* Normalizes a path, removing double and single dot path steps.
*
*
* THIS IS DIFFERENT in that it might not be a path that resolves to anything
*
*
* This method normalizes a path to a standard format.
* The input may contain separators in either Unix or Windows format.
* The output will contain separators in the format of the system.
*
*
* A trailing slash will be retained.
* A double slash will be merged to a single slash (but UNC names are handled).
* A single dot path segment will be removed.
* A double dot will cause that path segment and the one before to be removed.
* If the double dot has no parent path segment to work with, `null`
* is returned.
*
*
* The output will be the same on both Unix and Windows except
* for the separator character.
* <pre>
* /foo// --> /foo/
* /foo/./ --> /foo/
* /foo/../bar --> /bar
* /foo/../bar/ --> /bar/
* /foo/../bar/../baz --> /baz
* //foo//./bar --> /foo/bar
* /../ --> null
* ../foo --> null
* foo/bar/.. --> foo/
* foo/../../bar --> null
* foo/../bar --> bar
* //server/foo/../bar --> //server/bar
* //server/../bar --> null
* C:\foo\..\bar --> C:\bar
* C:\..\bar --> null
* ~/foo/../bar/ --> ~/bar/
* ~/../bar --> null
</pre> *
* (Note the file separator returned will be correct for Windows/Unix)
*
* @param filename the filename to normalize, null returns null
*
* @return the normalized filename, or null if invalid
*/
fun normalizeRaw(filename: String): String? {
return doNormalize(filename, SYSTEM_SEPARATOR, true)
}
/*
* FilenameUtils.java (normalize + dependencies) - Apache 2.0 License
* http://commons.apache.org/proper/commons-io/
* Copyright 2013 ASF
* Authors: Kevin A. Burton, Scott Sanders, Daniel Rall, Christoph.Reck,
* Peter Donald, Jeff Turner, Matthew Hawthorne, Martin Cooper,
* Jeremias Maerki, Stephen Colebourne
*/
/**
* Normalizes a path, removing double and single dot path steps.
*
*
* This method normalizes a path to a standard format.
* The input may contain separators in either Unix or Windows format.
* The output will contain separators in the format of the system.
*
*
* A trailing slash will be retained.
* A double slash will be merged to a single slash (but UNC names are handled).
* A single dot path segment will be removed.
* A double dot will cause that path segment and the one before to be removed.
* If the double dot has no parent path segment to work with, `null`
* is returned.
*
*
* The output will be the same on both Unix and Windows except
* for the separator character.
* <pre>
* /foo// --> /foo/
* /foo/./ --> /foo/
* /foo/../bar --> /bar
* /foo/../bar/ --> /bar/
* /foo/../bar/../baz --> /baz
* //foo//./bar --> /foo/bar
* /../ --> null
* ../foo --> null
* foo/bar/.. --> foo/
* foo/../../bar --> null
* foo/../bar --> bar
* //server/foo/../bar --> //server/bar
* //server/../bar --> null
* C:\foo\..\bar --> C:\bar
* C:\..\bar --> null
* ~/foo/../bar/ --> ~/bar/
* ~/../bar --> null
</pre> *
* (Note the file separator returned will be correct for Windows/Unix)
*
* @param filename the file to normalize, null returns null
* @return the normalized file, or null if invalid
*/
fun normalize(filename: String): File? {
val asString = doNormalize(File(filename).absolutePath, SYSTEM_SEPARATOR, true) ?: return null
return File(asString).absoluteFile
}
/*
* FilenameUtils.java (normalize + dependencies) - Apache 2.0 License
* http://commons.apache.org/proper/commons-io/
* Copyright 2013 ASF
* Authors: Kevin A. Burton, Scott Sanders, Daniel Rall, Christoph.Reck,
* Peter Donald, Jeff Turner, Matthew Hawthorne, Martin Cooper,
* Jeremias Maerki, Stephen Colebourne
*/
/**
* Normalizes a path, removing double and single dot path steps.
*
*
* This method normalizes a path to a standard format.
* The input may contain separators in either Unix or Windows format.
* The output will contain separators in the format of the system.
*
*
* A trailing slash will be retained.
* A double slash will be merged to a single slash (but UNC names are handled).
* A single dot path segment will be removed.
* A double dot will cause that path segment and the one before to be removed.
* If the double dot has no parent path segment to work with, `null`
* is returned.
*
*
* The output will be the same on both Unix and Windows except
* for the separator character.
* <pre>
* /foo// --> /foo/
* /foo/./ --> /foo/
* /foo/../bar --> /bar
* /foo/../bar/ --> /bar/
* /foo/../bar/../baz --> /baz
* //foo//./bar --> /foo/bar
* /../ --> null
* ../foo --> null
* foo/bar/.. --> foo/
* foo/../../bar --> null
* foo/../bar --> bar
* //server/foo/../bar --> //server/bar
* //server/../bar --> null
* C:\foo\..\bar --> C:\bar
* C:\..\bar --> null
* ~/foo/../bar/ --> ~/bar/
* ~/../bar --> null
</pre> *
* (Note the file separator returned will be correct for Windows/Unix)
*
* @param file the file to normalize, null returns null
* @return the normalized file, or null if invalid
*/
fun normalize(file: File): File? {
val asString = doNormalize(file.absolutePath, SYSTEM_SEPARATOR, true) ?: return null
return File(asString).absoluteFile
}
/*
* FilenameUtils.java (normalize + dependencies) - Apache 2.0 License
* http://commons.apache.org/proper/commons-io/
* Copyright 2013 ASF
* Authors: Kevin A. Burton, Scott Sanders, Daniel Rall, Christoph.Reck,
* Peter Donald, Jeff Turner, Matthew Hawthorne, Martin Cooper,
* Jeremias Maerki, Stephen Colebourne
*/
/**
* Normalizes a path, removing double and single dot path steps.
*
*
* This method normalizes a path to a standard format.
* The input may contain separators in either Unix or Windows format.
* The output will contain separators in the format specified.
*
*
* A trailing slash will be retained.
* A double slash will be merged to a single slash (but UNC names are handled).
* A single dot path segment will be removed.
* A double dot will cause that path segment and the one before to be removed.
* If the double dot has no parent path segment to work with, `null`
* is returned.
*
*
* The output will be the same on both Unix and Windows except
* for the separator character.
* <pre>
* /foo// --> /foo/
* /foo/./ --> /foo/
* /foo/../bar --> /bar
* /foo/../bar/ --> /bar/
* /foo/../bar/../baz --> /baz
* //foo//./bar --> /foo/bar
* /../ --> null
* ../foo --> null
* foo/bar/.. --> foo/
* foo/../../bar --> null
* foo/../bar --> bar
* //server/foo/../bar --> //server/bar
* //server/../bar --> null
* C:\foo\..\bar --> C:\bar
* C:\..\bar --> null
* ~/foo/../bar/ --> ~/bar/
* ~/../bar --> null
</pre> *
* The output will be the same on both Unix and Windows including
* the separator character.
*
* @param filename the filename to normalize, null returns null
* @param unixSeparator `true` if a unix separator should
* be used or `false` if a windows separator should be used.
* @return the normalized filename, or null if invalid
*/
fun normalize(filename: String, unixSeparator: Boolean): String? {
val separator = if (unixSeparator) UNIX_SEPARATOR else WINDOWS_SEPARATOR
return doNormalize(filename, separator, true)
}
//-----------------------------------------------------------------------
/*
* FilenameUtils.java (normalize + dependencies) - Apache 2.0 License
* http://commons.apache.org/proper/commons-io/
* Copyright 2013 ASF
* Authors: Kevin A. Burton, Scott Sanders, Daniel Rall, Christoph.Reck,
* Peter Donald, Jeff Turner, Matthew Hawthorne, Martin Cooper,
* Jeremias Maerki, Stephen Colebourne
*/
/**
* Normalizes a path, removing double and single dot path steps,
* and removing any final directory separator.
*
*
* This method normalizes a path to a standard format.
* The input may contain separators in either Unix or Windows format.
* The output will contain separators in the format of the system.
*
*
* A trailing slash will be removed.
* A double slash will be merged to a single slash (but UNC names are handled).
* A single dot path segment will be removed.
* A double dot will cause that path segment and the one before to be removed.
* If the double dot has no parent path segment to work with, `null`
* is returned.
*
*
* The output will be the same on both Unix and Windows except
* for the separator character.
* <pre>
* /foo// --> /foo
* /foo/./ --> /foo
* /foo/../bar --> /bar
* /foo/../bar/ --> /bar
* /foo/../bar/../baz --> /baz
* //foo//./bar --> /foo/bar
* /../ --> null
* ../foo --> null
* foo/bar/.. --> foo
* foo/../../bar --> null
* foo/../bar --> bar
* //server/foo/../bar --> //server/bar
* //server/../bar --> null
* C:\foo\..\bar --> C:\bar
* C:\..\bar --> null
* ~/foo/../bar/ --> ~/bar
* ~/../bar --> null
</pre> *
* (Note the file separator returned will be correct for Windows/Unix)
*
* @param filename the filename to normalize, null returns null
* @return the normalized filename, or null if invalid
*/
fun normalizeNoEndSeparator(filename: String): String? {
return doNormalize(filename, SYSTEM_SEPARATOR, false)
}
/*
* FilenameUtils.java (normalize + dependencies) - Apache 2.0 License
* http://commons.apache.org/proper/commons-io/
* Copyright 2013 ASF
* Authors: Kevin A. Burton, Scott Sanders, Daniel Rall, Christoph.Reck,
* Peter Donald, Jeff Turner, Matthew Hawthorne, Martin Cooper,
* Jeremias Maerki, Stephen Colebourne
*/
/**
* Normalizes a path, removing double and single dot path steps,
* and removing any final directory separator.
*
*
* This method normalizes a path to a standard format.
* The input may contain separators in either Unix or Windows format.
* The output will contain separators in the format specified.
*
*
* A trailing slash will be removed.
* A double slash will be merged to a single slash (but UNC names are handled).
* A single dot path segment will be removed.
* A double dot will cause that path segment and the one before to be removed.
* If the double dot has no parent path segment to work with, `null`
* is returned.
*
*
* The output will be the same on both Unix and Windows including
* the separator character.
* <pre>
* /foo// --> /foo
* /foo/./ --> /foo
* /foo/../bar --> /bar
* /foo/../bar/ --> /bar
* /foo/../bar/../baz --> /baz
* //foo//./bar --> /foo/bar
* /../ --> null
* ../foo --> null
* foo/bar/.. --> foo
* foo/../../bar --> null
* foo/../bar --> bar
* //server/foo/../bar --> //server/bar
* //server/../bar --> null
* C:\foo\..\bar --> C:\bar
* C:\..\bar --> null
* ~/foo/../bar/ --> ~/bar
* ~/../bar --> null
</pre> *
*
* @param filename the filename to normalize, null returns null
* @param unixSeparator `true` if a unix separator should
* be used or `false` if a windows separtor should be used.
* @return the normalized filename, or null if invalid
*/
fun normalizeNoEndSeparator(filename: String, unixSeparator: Boolean): String? {
val separator = if (unixSeparator) UNIX_SEPARATOR else WINDOWS_SEPARATOR
return doNormalize(filename, separator, false)
}
/*
* FilenameUtils.java (normalize + dependencies) - Apache 2.0 License
* http://commons.apache.org/proper/commons-io/
* Copyright 2013 ASF
* Authors: Kevin A. Burton, Scott Sanders, Daniel Rall, Christoph.Reck,
* Peter Donald, Jeff Turner, Matthew Hawthorne, Martin Cooper,
* Jeremias Maerki, Stephen Colebourne
*/
/**
* Internal method to perform the normalization.
*
* @param filename the filename
* @param separator The separator character to use
* @param keepSeparator true to keep the final separator
* @return the normalized filename
*/
private fun doNormalize(filename: String, separator: Char, keepSeparator: Boolean): String? {
var size = filename.length
if (size == 0) {
return filename
}
val prefix = getPrefixLength(filename)
if (prefix < 0) {
return null
}
val array = CharArray(size + 2) // +1 for possible extra slash, +2 for arraycopy
filename.toCharArray(array, 0, 0, filename.length)
// fix separators throughout
val otherSeparator = if (separator == SYSTEM_SEPARATOR) OTHER_SEPARATOR else SYSTEM_SEPARATOR
for (i in array.indices) {
if (array[i] == otherSeparator) {
array[i] = separator
}
}
// add extra separator on the end to simplify code below
var lastIsDirectory = true
if (array[size - 1] != separator) {
array[size++] = separator
lastIsDirectory = false
}
// adjoining slashes
run {
var i = prefix + 1
while (i < size) {
if (array[i] == separator && array[i - 1] == separator) {
System.arraycopy(array, i, array, i - 1, size - i)
size--
i--
}
i++
}
}
// dot slash
var i = prefix + 1
while (i < size) {
if (array[i] == separator && array[i - 1] == '.' && (i == prefix + 1 || array[i - 2] == separator)) {
if (i == size - 1) {
lastIsDirectory = true
}
System.arraycopy(array, i + 1, array, i - 1, size - i)
size -= 2
i--
}
i++
}
i = prefix + 2
outer@ while (i < size) {
if (array[i] == separator && array[i - 1] == '.' && array[i - 2] == '.' && (i == prefix + 2 || array[i - 3] == separator)) {
if (i == prefix + 2) {
return null
}
if (i == size - 1) {
lastIsDirectory = true
}
var j: Int
j = i - 4
while (j >= prefix) {
if (array[j] == separator) {
// remove b/../ from a/b/../c
System.arraycopy(array, i + 1, array, j + 1, size - i)
size -= i - j
i = j + 1
i++
continue@outer
}
j--
}
// remove a/../ from a/../c
System.arraycopy(array, i + 1, array, prefix, size - i)
size -= i + 1 - prefix
i = prefix + 1
}
i++
}
if (size <= 0) { // should never be less than 0
return ""
}
if (size <= prefix) { // should never be less than prefix
return String(array, 0, size)
}
return if (lastIsDirectory && keepSeparator) {
String(array, 0, size) // keep trailing separator
} else String(array, 0, size - 1)
// lose trailing separator
}
//-----------------------------------------------------------------------
/*
* FilenameUtils.java (normalize + dependencies) - Apache 2.0 License
* http://commons.apache.org/proper/commons-io/
* Copyright 2013 ASF
* Authors: Kevin A. Burton, Scott Sanders, Daniel Rall, Christoph.Reck,
* Peter Donald, Jeff Turner, Matthew Hawthorne, Martin Cooper,
* Jeremias Maerki, Stephen Colebourne
*/
/**
* Returns the length of the filename prefix, such as `C:/` or `~/`.
*
*
* This method will handle a file in either Unix or Windows format.
*
*
* The prefix length includes the first slash in the full filename
* if applicable. Thus, it is possible that the length returned is greater
* than the length of the input string.
````
Windows:
a\b\c.txt --> "" --> relative
\a\b\c.txt --> "\" --> current drive absolute
C:a\b\c.txt --> "C:" --> drive relative
C:\a\b\c.txt --> "C:\" --> absolute
\\server\a\b\c.txt --> "\\server\" --> UNC
Unix:
a/b/c.txt --> "" --> relative
/a/b/c.txt --> "/" --> absolute
~/a/b/c.txt --> "~/" --> current user
~ --> "~/" --> current user (slash added)
~user/a/b/c.txt --> "~user/" --> named user
~user --> "~user/" --> named user (slash added)
````
*
*
*
* The output will be the same irrespective of the machine that the code is running on.
* ie. both Unix and Windows prefixes are matched regardless.
*
* @param filename the filename to find the prefix in, null returns -1
* @return the length of the prefix, -1 if invalid or null
*/
fun getPrefixLength(filename: String): Int {
val len = filename.length
if (len == 0) {
return 0
}
var ch0 = filename[0]
if (ch0 == ':') {
return -1
}
return if (len == 1) {
if (ch0 == '~') {
return 2 // return a length greater than the input
}
if (isSeparator(ch0)) 1 else 0
} else {
if (ch0 == '~') {
var posUnix = filename.indexOf(UNIX_SEPARATOR, 1)
var posWin = filename.indexOf(WINDOWS_SEPARATOR, 1)
if (posUnix == -1 && posWin == -1) {
return len + 1 // return a length greater than the input
}
posUnix = if (posUnix == -1) posWin else posUnix
posWin = if (posWin == -1) posUnix else posWin
return Math.min(posUnix, posWin) + 1
}
val ch1 = filename[1]
if (ch1 == ':') {
ch0 = ch0.uppercaseChar()
if (ch0 >= 'A' && ch0 <= 'Z') {
return if (len == 2 || isSeparator(filename[2]) == false) {
2
} else 3
}
-1
} else if (isSeparator(ch0) && isSeparator(ch1)) {
var posUnix = filename.indexOf(UNIX_SEPARATOR, 2)
var posWin = filename.indexOf(WINDOWS_SEPARATOR, 2)
if (posUnix == -1 && posWin == -1 || posUnix == 2 || posWin == 2) {
return -1
}
posUnix = if (posUnix == -1) posWin else posUnix
posWin = if (posWin == -1) posUnix else posWin
Math.min(posUnix, posWin) + 1
} else {
if (isSeparator(ch0)) 1 else 0
}
}
}
//-----------------------------------------------------------------------
/*
* FilenameUtils.java (normalize + dependencies) - Apache 2.0 License
* http://commons.apache.org/proper/commons-io/
* Copyright 2013 ASF
* Authors: Kevin A. Burton, Scott Sanders, Daniel Rall, Christoph.Reck,
* Peter Donald, Jeff Turner, Matthew Hawthorne, Martin Cooper,
* Jeremias Maerki, Stephen Colebourne
*/
/**
* Checks if the character is a separator.
*
* @param ch the character to check
* @return true if it is a separator character
*/
private fun isSeparator(ch: Char): Boolean {
return ch == UNIX_SEPARATOR || ch == WINDOWS_SEPARATOR
}
/**
* Gets the extension of a file (text after the last '.')
*
* @return "" if there is no extension
*/
fun getExtension(fileName: String): String {
val dot = fileName.lastIndexOf('.')
return if (dot > -1) {
fileName.substring(dot + 1)
} else {
""
}
}
/**
* Gets the name of a file that is before the extension (text before the last '.')
*
* @return non-null
*/
fun getNameWithoutExtension(fileName: String): String {
val dot = fileName.lastIndexOf('.')
return if (dot > -1) {
fileName.substring(0, dot)
} else {
fileName
}
}
}

View File

@ -27,6 +27,11 @@ import java.net.URISyntaxException
*/
@Suppress("unused")
object FontUtil {
/**
* Gets the version number.
*/
val version = Sys.version
/** Default location where all the fonts are stored */
@Volatile
var FONTS_LOCATION = getProperty(FontUtil::class.java.canonicalName + ".FONTS_LOCATION", "resources/fonts")

View File

@ -1,81 +0,0 @@
/*
* Copyright 2023 dorkbox, llc
*
* 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.
*/
package dorkbox.util
import dorkbox.util.Sys.charToBytes16
import dorkbox.util.Sys.concatBytes
import org.bouncycastle.crypto.digests.SHA256Digest
/**
* Bouncycastle hashes!
*/
object HashBouncyUtil {
/**
* gets the SHA256 hash + SALT of the specified username, as UTF-16
*/
fun getSha256WithSalt(username: String?, saltBytes: ByteArray): ByteArray? {
if (username == null) {
return null
}
val charToBytes = charToBytes16(username.toCharArray())
val userNameWithSalt = concatBytes(charToBytes, saltBytes)
val sha256 = SHA256Digest()
val usernameHashBytes = ByteArray(sha256.digestSize)
sha256.update(userNameWithSalt, 0, userNameWithSalt.size)
sha256.doFinal(usernameHashBytes, 0)
return usernameHashBytes
}
/**
* gets the SHA256 hash of the specified string, as UTF-16
*/
fun getSha256(string: String): ByteArray {
val charToBytes = charToBytes16(string.toCharArray())
val sha256 = SHA256Digest()
val usernameHashBytes = ByteArray(sha256.digestSize)
sha256.update(charToBytes, 0, charToBytes.size)
sha256.doFinal(usernameHashBytes, 0)
return usernameHashBytes
}
/**
* gets the SHA256 hash of the specified byte array
*/
fun getSha256(bytes: ByteArray): ByteArray {
val sha256 = SHA256Digest()
val hashBytes = ByteArray(sha256.digestSize)
sha256.update(bytes, 0, bytes.size)
sha256.doFinal(hashBytes, 0)
return hashBytes
}
fun getSha256WithSalt(bytes: ByteArray?, saltBytes: ByteArray?): ByteArray? {
if (bytes == null || saltBytes == null) {
return null
}
val bytesWithSalt = concatBytes(bytes, saltBytes)
val sha256 = SHA256Digest()
val usernameHashBytes = ByteArray(sha256.digestSize)
sha256.update(bytesWithSalt, 0, bytesWithSalt.size)
sha256.doFinal(usernameHashBytes, 0)
return usernameHashBytes
}
}

View File

@ -1,93 +0,0 @@
/*
* Copyright 2023 dorkbox, llc
*
* 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.
*/
package dorkbox.util
import dorkbox.util.Sys.charToBytes16
import dorkbox.util.Sys.concatBytes
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
/**
* Java native hashes!
*/
object HashUtil {
private val digestLocal = ThreadLocal.withInitial {
try {
return@withInitial MessageDigest.getInstance("SHA-256")
} catch (e: NoSuchAlgorithmException) {
throw RuntimeException("Unable to initialize hash algorithm. SHA-256 digest doesn't exist?!? (This should not happen")
}
}
/**
* gets the SHA256 hash + SALT of the specified username, as UTF-16
*/
fun getSha256WithSalt(username: String?, saltBytes: ByteArray?): ByteArray? {
if (username == null) {
return null
}
val charToBytes = charToBytes16(username.toCharArray())
val userNameWithSalt = concatBytes(charToBytes, saltBytes!!)
val sha256 = digestLocal.get()
val usernameHashBytes = ByteArray(sha256.digestLength)
sha256.update(userNameWithSalt, 0, userNameWithSalt.size)
sha256.digest(usernameHashBytes)
return usernameHashBytes
}
/**
* gets the SHA256 hash of the specified string, as UTF-16
*/
fun getSha256(string: String): ByteArray {
val charToBytes = charToBytes16(string.toCharArray())
val sha256 = digestLocal.get()
val usernameHashBytes = ByteArray(sha256.digestLength)
sha256.update(charToBytes, 0, charToBytes.size)
sha256.digest(usernameHashBytes)
return usernameHashBytes
}
/**
* gets the SHA256 hash of the specified byte array
*/
fun getSha256(bytes: ByteArray): ByteArray {
val sha256 = digestLocal.get()
val hashBytes = ByteArray(sha256.digestLength)
sha256.update(bytes, 0, bytes.size)
sha256.digest(hashBytes)
return hashBytes
}
fun getSha256WithSalt(bytes: ByteArray?, saltBytes: ByteArray?): ByteArray? {
if (bytes == null || saltBytes == null) {
return null
}
val bytesWithSalt = concatBytes(bytes, saltBytes)
val sha256 = digestLocal.get()
val usernameHashBytes = ByteArray(sha256.digestLength)
sha256.update(bytesWithSalt, 0, bytesWithSalt.size)
sha256.digest(usernameHashBytes)
return usernameHashBytes
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010 dorkbox, llc
* Copyright 2023 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -25,13 +25,13 @@ import javax.imageio.stream.ImageInputStream;
@SuppressWarnings({"unused", "Duplicates"})
// deprecated. use kotlin.
@Deprecated
public
class IO {
/**
* Convenient close for a Closeable.
*/
@SuppressWarnings("Duplicates")
@Deprecated
public static
void close(final Closeable closeable) {
if (closeable != null) {
@ -48,6 +48,7 @@ class IO {
* Convenient close for a Closeable.
*/
@SuppressWarnings("Duplicates")
@Deprecated
public static
void closeQuietly(final Closeable closeable) {
if (closeable != null) {
@ -61,8 +62,9 @@ class IO {
/**
* Copy the contents of the input stream to the output stream.
* <p>
* DOES NOT CLOSE THE STEAMS!
* DOES NOT CLOSE THE STREAMS!
*/
@Deprecated
public static
<T extends OutputStream> T copyStream(final InputStream inputStream, final T outputStream) throws IOException {
byte[] buffer = new byte[4096];
@ -79,8 +81,9 @@ class IO {
/**
* Copy the contents of the input stream to the output stream.
* <p>
* DOES NOT CLOSE THE STEAMS!
* DOES NOT CLOSE THE STREAMS!
*/
@Deprecated
public static
<T extends OutputStream> T copyStream(final ImageInputStream inputStream, final T outputStream) throws IOException {
return copyStream(4096, inputStream, outputStream);
@ -89,8 +92,9 @@ class IO {
/**
* Copy the contents of the input stream to the output stream.
* <p>
* DOES NOT CLOSE THE STEAMS!
* DOES NOT CLOSE THE STREAMS!
*/
@Deprecated
public static
<T extends OutputStream> T copyStream(final int bufferSize, final ImageInputStream inputStream, final T outputStream) throws IOException {
byte[] buffer = new byte[bufferSize];
@ -107,8 +111,9 @@ class IO {
/**
* Copy the contents of the input stream to a new output stream.
* <p>
* DOES NOT CLOSE THE STEAMS!
* DOES NOT CLOSE THE STREAMS!
*/
@Deprecated
public static
ByteArrayOutputStream copyStream(final InputStream inputStream) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(4096);
@ -126,8 +131,9 @@ class IO {
/**
* Copy the contents of the input stream to a new output stream.
* <p>
* DOES NOT CLOSE THE STEAMS!
* DOES NOT CLOSE THE STREAMS!
*/
@Deprecated
public static
ByteArrayOutputStream copyStream(final ImageInputStream inputStream) throws IOException {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(4096);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2016 dorkbox, llc
* Copyright 2023 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -145,7 +145,7 @@ class ImageUtil {
// have to resize the file (and return the new path)
String extension = FileUtil.INSTANCE.getExtension(fileName);
String extension = Sys.INSTANCE.getExtension(fileName);
if (extension.isEmpty()) {
extension = "png"; // made up
}
@ -162,7 +162,7 @@ class ImageUtil {
}
else {
// suck it out of a URL/Resource (with debugging if necessary)
final URL systemResource = LocationResolver.getResource(fileName);
final URL systemResource = LocationResolver.Companion.getResource(fileName);
image = new ImageIcon(systemResource).getImage();
}

View File

@ -1,46 +0,0 @@
/*
* Copyright 2010 dorkbox, llc
*
* 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.
*/
package dorkbox.util
import org.tukaani.xz.LZMA2Options
import org.tukaani.xz.LZMAInputStream
import org.tukaani.xz.LZMAOutputStream
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
object LZMA {
// https://tukaani.org/xz/java.html
@Throws(IOException::class)
fun encode(input: InputStream, output: OutputStream) {
LZMAOutputStream(output, LZMA2Options(3), true).use { compressionStream ->
input.copyTo(compressionStream)
}
}
@Throws(IOException::class)
fun decode(input: InputStream): ByteArrayOutputStream {
val byteArrayOutputStream = ByteArrayOutputStream(8192)
LZMAInputStream(input).use { compressedStream -> compressedStream.copyTo(byteArrayOutputStream) }
return byteArrayOutputStream
}
@Throws(IOException::class)
fun decode(input: InputStream, output: OutputStream) {
LZMAInputStream(input).use { compressedStream -> compressedStream.copyTo(output) }
}
}

View File

@ -222,6 +222,11 @@ class LocationResolver {
}
companion object {
/**
* Gets the version number.
*/
val version = Sys.version
private val SLASH_PATTERN = Pattern.compile("\\\\")
private fun log(message: String) {
@ -280,7 +285,6 @@ class LocationResolver {
*
* @return the URL for that given resource name
*/
@JvmStatic
fun getResource(resourceName: String): URL? {
var resourceName = resourceName
try {

View File

@ -16,6 +16,11 @@
package dorkbox.util
object MathUtil {
/**
* Gets the version number.
*/
val version = Sys.version
/**
* Checks to see if the string is an integer
*

View File

@ -1262,6 +1262,11 @@ open class MersenneTwisterFast {
}
companion object {
/**
* Gets the version number.
*/
val version = Sys.version
// Serialization
private const val serialVersionUID = -8219700664442619525L // locked as of Version 15

View File

@ -1,20 +0,0 @@
/*
* Copyright 2010 dorkbox, llc
*
* 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.
*/
package dorkbox.util;
public interface Message {
}

View File

@ -45,6 +45,14 @@ class NamedThreadFactory(
constructor(poolNamePrefix: String, group: ThreadGroup, threadPriority: Int, isDaemon: Boolean) : this(poolNamePrefix, group, threadPriority, isDaemon, {})
constructor(poolNamePrefix: String, group: ThreadGroup, isDaemon: Boolean, actionOnNewThread: (Thread) -> Unit) : this(poolNamePrefix, group, Thread.NORM_PRIORITY, isDaemon, actionOnNewThread)
companion object {
/**
* Gets the version number.
*/
val version = Sys.version
}
private val poolId = AtomicInteger()

View File

@ -26,6 +26,11 @@ import java.security.PrivilegedAction
* Loads the specified library, extracting it from the jar, if necessary
*/
object NativeLoader {
/**
* Gets the version number.
*/
val version = Sys.version
@Throws(IOException::class)
fun extractLibrary(sourceFileName: String, destinationDirectory: String?, destinationName: String, version: String?): File {
return try {

View File

@ -19,6 +19,11 @@ package dorkbox.util
* This class uses the MersenneTwisterFast, which is MOSTLY random.
*/
object RandomUtil {
/**
* Gets the version number.
*/
val version = Sys.version
private val random: FastThreadLocal<MersenneTwisterFast> = object : FastThreadLocal<MersenneTwisterFast>() {
override fun initialValue(): MersenneTwisterFast {
return MersenneTwisterFast()

View File

@ -16,6 +16,11 @@
package dorkbox.util
object RegExp {
/**
* Gets the version number.
*/
val version = Sys.version
private const val whitespace_chars = ("" /* dummy empty string for homogeneity */ + "\\u0009" // CHARACTER TABULATION
+ "\\u000A" // LINE FEED (LF)
+ "\\u000B" // LINE TABULATION

View File

@ -21,6 +21,11 @@ import java.awt.*
* Screen utilities
*/
object ScreenUtil {
/**
* Gets the version number.
*/
val version = Sys.version
/**
* @return the screen bounds for the monitor at a specific point. Will return null if there is no screen at the point

View File

@ -26,6 +26,11 @@ import javax.swing.*
@Suppress("unused")
object SwingUtil {
/**
* Gets the version number.
*/
val version = Sys.version
init {/*
* hack workaround for starting the Toolkit thread before any Timer stuff javax.swing.Timer uses the Event Dispatch Thread, which is not
* created until the Toolkit thread starts up. Using the Swing Timer before starting this stuff starts up may get unexpected

View File

@ -15,7 +15,6 @@
*/
package dorkbox.util
import dorkbox.os.OS.LINE_SEPARATOR
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.io.InputStream
@ -24,55 +23,22 @@ import java.util.concurrent.*
@Suppress("unused")
object Sys {
/**
* Gets the version number.
*/
val version = "1.48"
init {
// Add this project to the updates system, which verifies this class + UUID + version information
dorkbox.updates.Updates.add(Sys::class.java, "aebbb926aeb144739e9f3cab90ffaa72", version)
}
const val KILOBYTE = 1024
const val MEGABYTE = 1024 * KILOBYTE
const val GIGABYTE = 1024 * MEGABYTE
const val TERABYTE = 1024L * GIGABYTE
val HEX_CHARS = charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F')
fun convertStringToChars(string: String): CharArray {
val charArray = string.toCharArray()
eraseString(string)
return charArray
}
fun eraseString(string: String?) {
// You can change the value of the inner char[] using reflection.
//
// You must be careful to either change it with an array of the same length,
// or to also update the count field.
//
// If you want to be able to use it as an entry in a set or as a value in map,
// you will need to recalculate the hash code and set the value of the hashCode field.
try {
val valueField = String::class.java.getDeclaredField("value")
valueField.isAccessible = true
val chars = valueField[string] as CharArray
Arrays.fill(chars, '*') // asterisk it out in case of GC not picking up the old char array.
valueField[string] = CharArray(0) // replace it.
// set count to 0
try {
// newer versions of java don't have this field
val countField = String::class.java.getDeclaredField("count")
countField.isAccessible = true
countField[string] = 0
} catch (ignored: Exception) {
}
// set hash to 0
val hashField = String::class.java.getDeclaredField("hash")
hashField.isAccessible = true
hashField[string] = 0
} catch (e: SecurityException) {
e.printStackTrace()
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalArgumentException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
}
/**
* FROM: https://www.cqse.eu/en/blog/string-replace-performance/
@ -151,22 +117,22 @@ object Sys {
fun getTimePretty(nanoSeconds: Long): String {
val unit: TimeUnit
val text: String
if (TimeUnit.DAYS.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0) {
if (TimeUnit.DAYS.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0L) {
unit = TimeUnit.DAYS
text = "d"
} else if (TimeUnit.HOURS.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0) {
} else if (TimeUnit.HOURS.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0L) {
unit = TimeUnit.HOURS
text = "h"
} else if (TimeUnit.MINUTES.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0) {
} else if (TimeUnit.MINUTES.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0L) {
unit = TimeUnit.MINUTES
text = "min"
} else if (TimeUnit.SECONDS.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0) {
text = "m"
} else if (TimeUnit.SECONDS.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0L) {
unit = TimeUnit.SECONDS
text = "s"
} else if (TimeUnit.MILLISECONDS.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0) {
} else if (TimeUnit.MILLISECONDS.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0L) {
unit = TimeUnit.MILLISECONDS
text = "ms"
} else if (TimeUnit.MICROSECONDS.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0) {
} else if (TimeUnit.MICROSECONDS.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0L) {
unit = TimeUnit.MICROSECONDS
text = "\u03bcs" // μs
} else {
@ -176,7 +142,16 @@ object Sys {
// convert the unit into the largest time unit possible (since that is often what makes sense)
val value = nanoSeconds.toDouble() / TimeUnit.NANOSECONDS.convert(1, unit)
return String.format("%.4g$text", value)
return if (value < 10) {
String.format("%.1g $text", value)
} else if (value < 100) {
String.format("%.2g $text", value)
} else if (value < 1000) {
String.format("%.3g $text", value)
} else {
String.format("%.4g $text", value)
}
}
/**
@ -185,22 +160,22 @@ object Sys {
fun getTimePrettyFull(nanoSeconds: Long): String {
val unit: TimeUnit
var text: String
if (TimeUnit.DAYS.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0) {
if (TimeUnit.DAYS.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0L) {
unit = TimeUnit.DAYS
text = "day"
} else if (TimeUnit.HOURS.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0) {
} else if (TimeUnit.HOURS.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0L) {
unit = TimeUnit.HOURS
text = "hour"
} else if (TimeUnit.MINUTES.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0) {
} else if (TimeUnit.MINUTES.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0L) {
unit = TimeUnit.MINUTES
text = "minute"
} else if (TimeUnit.SECONDS.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0) {
} else if (TimeUnit.SECONDS.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0L) {
unit = TimeUnit.SECONDS
text = "second"
} else if (TimeUnit.MILLISECONDS.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0) {
} else if (TimeUnit.MILLISECONDS.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0L) {
unit = TimeUnit.MILLISECONDS
text = "milli-second"
} else if (TimeUnit.MICROSECONDS.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0) {
} else if (TimeUnit.MICROSECONDS.convert(nanoSeconds, TimeUnit.NANOSECONDS) > 0L) {
unit = TimeUnit.MICROSECONDS
text = "micro-second"
} else {
@ -213,12 +188,49 @@ object Sys {
if (value > 1.0) {
text += "s"
}
return String.format("%.4g $text", value)
return if (value < 10) {
String.format("%.1g $text", value)
} else if (value < 100) {
String.format("%.2g $text", value)
} else if (value < 1000) {
String.format("%.3g $text", value)
} else {
String.format("%.4g $text", value)
}
}
private fun <T : Throwable> throwException0(t: Throwable) {
@Suppress("UNCHECKED_CAST")
throw t as T
}
/**
* Converts a Thrown exception, to bypasses the compiler checks for the checked exception. This uses type erasure to work.
*/
fun Throwable.unchecked() {
throwException0<RuntimeException>(this)
}
/**
* Gets the extension of a file (text after the last '.')
*
* @return "" if there is no extension
*/
@Deprecated("Use kotlin")
fun getExtension(fileName: String): String {
val dot = fileName.lastIndexOf('.')
return if (dot > -1) {
fileName.substring(dot + 1)
} else {
""
}
}
/**
* Convert the contents of the input stream to a byte array.
*/
@Deprecated("Use kotlin")
@Throws(IOException::class)
fun getBytesFromStream(inputStream: InputStream): ByteArray {
val baos = ByteArrayOutputStream(8192)
@ -232,6 +244,7 @@ object Sys {
return baos.toByteArray()
}
@Deprecated("Use kotlin")
@JvmOverloads
fun copyBytes(src: ByteArray, position: Int = 0): ByteArray {
val length = src.size - position
@ -240,7 +253,7 @@ object Sys {
return b
}
@JvmStatic
@Deprecated("Use kotlin")
fun concatBytes(vararg arrayBytes: ByteArray): ByteArray {
var length = 0
for (bytes in arrayBytes) {
@ -254,342 +267,46 @@ object Sys {
}
return concatBytes
}
}
/**
* this saves the char array in UTF-16 format of bytes
*/
@JvmStatic
fun charToBytes16(text: CharArray): ByteArray {
// NOTE: this saves the char array in UTF-16 format of bytes.
val bytes = ByteArray(text.size * 2)
for (i in text.indices) {
bytes[2 * i] = (text[i].code shr 8).toByte()
bytes[2 * i + 1] = text[i].code.toByte()
}
return bytes
}
/**
* Erase the contents of a string, in-memory. This has no effect if the string has been interned.
*/
fun String.eraseString() {
// You can change the value of the inner char[] using reflection.
//
// You must be careful to either change it with an array of the same length,
// or to also update the count field.
//
// If you want to be able to use it as an entry in a set or as a value in map,
// you will need to recalculate the hash code and set the value of the hashCode field.
try {
val valueField = String::class.java.getDeclaredField("value")
valueField.isAccessible = true
val chars = valueField[this] as CharArray
Arrays.fill(chars, '*') // asterisk it out in case of GC not picking up the old char array.
valueField[this] = CharArray(0) // replace it.
fun intsToBytes(ints: IntArray): ByteArray {
val length = ints.size
val bytes = ByteArray(length)
for (i in 0 until length) {
val intValue = ints[i]
if (intValue < 0 || intValue > 255) {
System.err.println("WARNING: int at index $i($intValue) was not a valid byte value (0-255)")
return ByteArray(length)
}
bytes[i] = intValue.toByte()
// set count to 0
try {
// newer versions of java don't have this field
val countField = String::class.java.getDeclaredField("count")
countField.isAccessible = true
countField[this] = 0
} catch (ignored: Exception) {
}
return bytes
}
fun charToBytesRaw(chars: CharArray): ByteArray {
val length = chars.size
val bytes = ByteArray(length)
for (i in 0 until length) {
val charValue = chars[i]
bytes[i] = charValue.code.toByte()
}
return bytes
}
fun bytesToInts(bytes: ByteArray, startPosition: Int, length: Int): IntArray {
val ints = IntArray(length)
val endPosition = startPosition + length
for (i in startPosition until endPosition) {
ints[i] = bytes[i].toInt() and 0xFF
}
return ints
}
@JvmOverloads
fun bytesToHex(bytes: ByteArray, startPosition: Int = 0, length: Int = bytes.size, padding: Boolean = false): String {
val endPosition = startPosition + length
return if (padding) {
val hexString = CharArray(3 * length)
var j = 0
for (i in startPosition until endPosition) {
hexString[j++] = HEX_CHARS[bytes[i].toInt() and 0xF0 shr 4]
hexString[j++] = HEX_CHARS[bytes[i].toInt() and 0x0F]
hexString[j++] = ' '
}
String(hexString)
} else {
val hexString = CharArray(2 * length)
var j = 0
for (i in startPosition until endPosition) {
hexString[j++] = HEX_CHARS[bytes[i].toInt() and 0xF0 shr 4]
hexString[j++] = HEX_CHARS[bytes[i].toInt() and 0x0F]
}
String(hexString)
}
}
/**
* Converts an ASCII character representing a hexadecimal
* value into its integer equivalent.
*/
fun hexByteToInt(b: Byte): Int {
return when (b.toInt()) {
'0'.code -> 0
'1'.code -> 1
'2'.code -> 2
'3'.code -> 3
'4'.code -> 4
'5'.code -> 5
'6'.code -> 6
'7'.code -> 7
'8'.code -> 8
'9'.code -> 9
'A'.code, 'a'.code -> 10
'B'.code, 'b'.code -> 11
'C'.code, 'c'.code -> 12
'D'.code, 'd'.code -> 13
'E'.code, 'e'.code -> 14
'F'.code, 'f'.code -> 15
else -> throw IllegalArgumentException("Error decoding byte")
}
}
/**
* Converts an ASCII character representing a hexadecimal
* value into its integer equivalent.
*/
fun hexCharToInt(b: Char): Int {
return when (b) {
'0' -> 0
'1' -> 1
'2' -> 2
'3' -> 3
'4' -> 4
'5' -> 5
'6' -> 6
'7' -> 7
'8' -> 8
'9' -> 9
'A', 'a' -> 10
'B', 'b' -> 11
'C', 'c' -> 12
'D', 'd' -> 13
'E', 'e' -> 14
'F', 'f' -> 15
else -> throw IllegalArgumentException("Error decoding byte")
}
}
/**
* A 4-digit hex result.
*/
fun hex4(c: Char, sb: StringBuilder) {
sb.append(HEX_CHARS[c.code and 0xF000 shr 12])
sb.append(HEX_CHARS[c.code and 0x0F00 shr 8])
sb.append(HEX_CHARS[c.code and 0x00F0 shr 4])
sb.append(HEX_CHARS[c.code and 0x000F])
}
/**
* Returns a string representation of the byte array as a series of
* hexadecimal characters.
*
* @param bytes byte array to convert
* @return a string representation of the byte array as a series of
* hexadecimal characters
*/
fun toHexString(bytes: ByteArray): String {
val hexString = CharArray(2 * bytes.size)
var j = 0
for (i in bytes.indices) {
hexString[j++] = HEX_CHARS[bytes[i].toInt() and 0xF0 shr 4]
hexString[j++] = HEX_CHARS[bytes[i].toInt() and 0x0F]
}
return String(hexString)
}
/**
* from netty 4.1, apache 2.0, https://netty.io
*/
fun hexToByte(s: CharSequence, pos: Int): Byte {
val hi = hexCharToInt(s[pos])
val lo = hexCharToInt(s[pos + 1])
require(!(hi == -1 || lo == -1)) {
String.format(
"invalid hex byte '%s' at index %d of '%s'", s.subSequence(pos, pos + 2), pos, s
)
}
return ((hi shl 4) + lo).toByte()
}
/**
* Decodes a string with [hex dump](http://en.wikipedia.org/wiki/Hex_dump)
*
* @param hex a [CharSequence] which contains the hex dump
*/
fun hexToBytes(hex: CharSequence): ByteArray {
return hexToBytes(hex, 0, hex.length)
}
/**
* Decodes part of a string with [hex dump](http://en.wikipedia.org/wiki/Hex_dump)
*
* from netty 4.1, apache 2.0, https://netty.io
*
* @param hexDump a [CharSequence] which contains the hex dump
* @param fromIndex start of hex dump in `hexDump`
* @param length hex string length
*/
fun hexToBytes(hexDump: CharSequence, fromIndex: Int, length: Int): ByteArray {
require(!(length < 0 || length and 1 != 0)) { "length: $length" }
if (length == 0) {
return ByteArray(0)
}
val bytes = ByteArray(length ushr 1)
var i = 0
while (i < length) {
bytes[i ushr 1] = hexToByte(hexDump, fromIndex + i)
i += 2
}
return bytes
}
/**
* XOR two byte arrays together, and save result in originalArray
*
* @param originalArray this is the base of the XOR operation.
* @param keyArray this is XOR'd into the original array, repeats if necessary.
*/
fun xorArrays(originalArray: ByteArray, keyArray: ByteArray) {
var keyIndex = 0
val keyLength = keyArray.size
for (i in originalArray.indices) {
//XOR the data and start over if necessary
originalArray[i] = (originalArray[i].toInt() xor keyArray[keyIndex++ % keyLength].toInt()).toByte()
}
}
fun encodeStringArray(array: List<String>): ByteArray {
var length = 0
for (s in array) {
val bytes = s.toByteArray()
length += bytes.size
}
if (length == 0) {
return ByteArray(0)
}
val bytes = ByteArray(length + array.size)
length = 0
for (s in array) {
val sBytes = s.toByteArray()
System.arraycopy(sBytes, 0, bytes, length, sBytes.size)
length += sBytes.size
bytes[length++] = 0x01.toByte()
}
return bytes
}
fun decodeStringArray(bytes: ByteArray): ArrayList<String> {
val length = bytes.size
var position = 0
val token = 0x01.toByte()
val list = ArrayList<String>(0)
var last = 0
while (last + position < length) {
val b = bytes[last + position++]
if (b == token) {
val xx = ByteArray(position - 1)
System.arraycopy(bytes, last, xx, 0, position - 1)
list.add(String(xx))
last += position
position = 0
}
}
return list
}
@JvmOverloads
fun printArrayRaw(bytes: ByteArray, lineLength: Int = 0): String {
return if (lineLength > 0) {
val length = bytes.size
val comma = length - 1
val builder = StringBuilder(length + length / lineLength)
for (i in 0 until length) {
builder.append(bytes[i].toInt())
if (i < comma) {
builder.append(",")
}
if (i > 0 && i % lineLength == 0) {
builder.append(LINE_SEPARATOR)
}
}
builder.toString()
} else {
val length = bytes.size
val comma = length - 1
val builder = StringBuilder(length + length)
for (i in 0 until length) {
builder.append(bytes[i].toInt())
if (i < comma) {
builder.append(",")
}
}
builder.toString()
}
}
@JvmOverloads
fun printArray(bytes: ByteArray, length: Int = bytes.size, includeByteCount: Boolean = true) {
printArray(bytes, 0, length, includeByteCount, 40, null)
}
@JvmOverloads
fun printArray(
bytes: ByteArray,
inputOffset: Int,
length: Int,
includeByteCount: Boolean,
lineLength: Int = 40,
header: String? = null
) {
val comma = length - 1
var builderLength = length + comma + 2
if (includeByteCount) {
builderLength += 7 + Integer.toString(length).length
}
if (lineLength > 0) {
builderLength += length / lineLength
}
if (header != null) {
builderLength += header.length + 2
}
val builder = StringBuilder(builderLength)
if (header != null) {
builder.append(header).append(LINE_SEPARATOR)
}
if (includeByteCount) {
builder.append("Bytes: ").append(length).append(LINE_SEPARATOR)
}
builder.append("{")
for (i in inputOffset until length) {
builder.append(bytes[i].toInt())
if (i < comma) {
builder.append(",")
}
if (i > inputOffset && lineLength > 0 && i % lineLength == 0) {
builder.append(LINE_SEPARATOR)
}
}
builder.append("}")
System.err.println(builder.toString())
}
/**
* Raises an exception, but bypasses the compiler checks for the checked exception. This uses type erasure to work
*/
fun throwException(t: Throwable) {
throwException0<RuntimeException>(t)
}
private fun <T : Throwable> throwException0(t: Throwable) {
@Suppress("UNCHECKED_CAST")
throw t as T
// set hash to 0
val hashField = String::class.java.getDeclaredField("hash")
hashField.isAccessible = true
hashField[this] = 0
} catch (e: SecurityException) {
e.printStackTrace()
} catch (e: NoSuchFieldException) {
e.printStackTrace()
} catch (e: IllegalArgumentException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
}

View File

@ -1,199 +0,0 @@
/*
* Copyright 2018 dorkbox, llc.
*
* 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.
*/
package dorkbox.util.classes;
import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.lang.reflect.WildcardType;
import java.util.Objects;
import net.jodah.typetools.TypeResolver;
public final
class ClassHelper {
/**
* Retrieves the generic type parameter for the PARENT (super) class of the specified class or lambda expression.
*
* Because of how type erasure works in java, this will work on lambda expressions and ONLY parent/super classes.
*
* @param genericTypeClass this class is what you are looking for
* @param classToCheck class to actually get the parameter from
* @param genericParameterToGet 0-based index of parameter as class to get
*
* @return null if the generic type could not be found.
*/
@SuppressWarnings({"StatementWithEmptyBody", "UnnecessaryLocalVariable"})
public static
Class<?> getGenericParameterAsClassForSuperClass(Class<?> genericTypeClass, Class<?> classToCheck, int genericParameterToGet) {
Class<?> loopClassCheck = classToCheck;
// this will ALWAYS return something, if it is unknown, it will return TypeResolver.Unknown.class
Class<?>[] classes = TypeResolver.resolveRawArguments(genericTypeClass, loopClassCheck);
if (classes.length > genericParameterToGet && classes[genericParameterToGet] != TypeResolver.Unknown.class) {
return classes[genericParameterToGet];
}
// case of multiple inheritance, we are trying to get the first available generic info
// don't check for Object.class (this is where superclass is null)
while (loopClassCheck != Object.class) {
// check to see if we have what we are looking for on our CURRENT class
Type superClassGeneric = loopClassCheck.getGenericSuperclass();
classes = TypeResolver.resolveRawArguments(superClassGeneric, loopClassCheck);
if (classes.length > genericParameterToGet) {
Class<?> aClass = classes[genericParameterToGet];
if (aClass != TypeResolver.Unknown.class) {
return classes[genericParameterToGet];
}
}
// NO MATCH, so walk up.
loopClassCheck = loopClassCheck.getSuperclass();
}
// NOTHING! now check interfaces!
loopClassCheck = classToCheck;
while (loopClassCheck != Object.class) {
// check to see if we have what we are looking for on our CURRENT class interfaces
Type[] genericInterfaces = loopClassCheck.getGenericInterfaces();
for (Type genericInterface : genericInterfaces) {
classes = TypeResolver.resolveRawArguments(genericInterface, loopClassCheck);
if (classes.length > genericParameterToGet) {
Class<?> aClass = classes[genericParameterToGet];
if (aClass != TypeResolver.Unknown.class) {
return aClass;
}
}
}
// NO MATCH, so walk up.
loopClassCheck = loopClassCheck.getSuperclass();
}
// couldn't find it.
return null;
}
// from: https://github.com/square/retrofit/blob/108fe23964b986107aed352ba467cd2007d15208/retrofit/src/main/java/retrofit2/Utils.java
public static Class<?> getRawType(Type type) {
Objects.requireNonNull(type, "type == null");
if (type instanceof Class<?>) {
// Type is a normal class.
return (Class<?>) type;
}
if (type instanceof ParameterizedType) {
ParameterizedType parameterizedType = (ParameterizedType) type;
// I'm not exactly sure why getRawType() returns Type instead of Class. Neal isn't either but
// suspects some pathological case related to nested classes exists.
Type rawType = parameterizedType.getRawType();
if (!(rawType instanceof Class)) throw new IllegalArgumentException();
return (Class<?>) rawType;
}
if (type instanceof GenericArrayType) {
Type componentType = ((GenericArrayType) type).getGenericComponentType();
return Array.newInstance(getRawType(componentType), 0).getClass();
}
if (type instanceof TypeVariable) {
// We could use the variable's bounds, but that won't work if there are multiple. Having a raw
// type that's more general than necessary is okay.
return Object.class;
}
if (type instanceof WildcardType) {
return getRawType(((WildcardType) type).getUpperBounds()[0]);
}
throw new IllegalArgumentException(
"Expected a Class, ParameterizedType, or "
+ "GenericArrayType, but <"
+ type
+ "> is of type "
+ type.getClass().getName());
}
/**
* Check to see if clazz or interface directly has one of the interfaces defined by requiredClass
* <p/>
* If the class DOES NOT directly have the interface it will fail.
*/
public static
boolean hasInterface(Class<?> requiredClass, Class<?> clazz) {
if (requiredClass == clazz) {
return true;
}
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> iface : interfaces) {
if (iface == requiredClass) {
return true;
}
}
// now walk up to see if we can find it.
for (Class<?> iface : interfaces) {
boolean b = hasInterface(requiredClass, iface);
if (b) {
return b;
}
}
// nothing, so now we check the PARENT of this class
Class<?> superClass = clazz.getSuperclass();
// case of multiple inheritance, we are trying to get the first available generic info
// don't check for Object.class (this is where superclass is null)
while (superClass != null && superClass != Object.class) {
// check to see if we have what we are looking for on our CURRENT class
if (hasInterface(requiredClass, superClass)) {
return true;
}
// NO MATCH, so walk up.
superClass = superClass.getSuperclass();
}
// if we don't find it.
return false;
}
/**
* Checks to see if the clazz is a subclass of a parent class.
*/
@SuppressWarnings("SimplifiableIfStatement")
public static
boolean hasParentClass(Class<?> parentClazz, Class<?> clazz) {
Class<?> superClass = clazz.getSuperclass();
if (parentClazz == superClass) {
return true;
}
if (superClass != null && superClass != Object.class) {
return hasParentClass(parentClazz, superClass);
}
return false;
}
private
ClassHelper() {
}
}

View File

@ -1,187 +0,0 @@
/*
* Copyright 2015 dorkbox, llc
*
* 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.
*/
package dorkbox.util.classes;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import dorkbox.collections.IdentityMap;
/**
* @author dorkbox
* Date: 4/1/15
*/
public final
class ClassHierarchy {
private volatile IdentityMap<Class<?>, Class<?>> arrayCache;
private volatile IdentityMap<Class<?>, Class<?>[]> superClassesCache;
// Recommended for best performance while adhering to the "single writer principle". Must be static-final
private static final AtomicReferenceFieldUpdater<ClassHierarchy, IdentityMap> arrayREF =
AtomicReferenceFieldUpdater.newUpdater(ClassHierarchy.class,
IdentityMap.class,
"arrayCache");
private static final AtomicReferenceFieldUpdater<ClassHierarchy, IdentityMap> superClassesREF =
AtomicReferenceFieldUpdater.newUpdater(ClassHierarchy.class,
IdentityMap.class,
"superClassesCache");
/**
* These data structures are never reset because the class hierarchy doesn't change at runtime. This class uses the "single writer
* principle" for storing data, EVEN THOUGH it's not accessed by a single writer. This DOES NOT MATTER because duplicates DO NOT matter
*/
public
ClassHierarchy(float loadFactor) {
this.arrayCache = new IdentityMap<Class<?>, Class<?>>(32, loadFactor);
this.superClassesCache = new IdentityMap<Class<?>, Class<?>[]>(32, loadFactor);
}
/**
* will return the class + parent classes as an array.
* if parameter clazz is of type array, then the super classes are of array type as well
* <p>
* race conditions will result in DUPLICATE answers, which we don't care if happens
* never returns null
* never reset (class hierarchy never changes during runtime)
*/
public
Class<?>[] getClassAndSuperClasses(final Class<?> clazz) {
// access a snapshot of the subscriptions (single-writer-principle)
final IdentityMap<Class<?>, Class<?>[]> cache = cast(superClassesREF.get(this));
Class<?>[] classes = cache.get(clazz);
// duplicates DO NOT MATTER
if (classes == null) {
// publish all super types of class
final Iterator<Class<?>> superTypesIterator = getSuperTypes(clazz);
final ArrayList<Class<?>> newList = new ArrayList<Class<?>>(16);
Class<?> c;
final boolean isArray = clazz.isArray();
// have to add the original class to the front of the list
newList.add(clazz);
if (isArray) {
// super-types for an array ALSO must be an array.
while (superTypesIterator.hasNext()) {
c = superTypesIterator.next();
c = getArrayClass(c);
if (c != clazz) {
newList.add(c);
}
}
}
else {
while (superTypesIterator.hasNext()) {
c = superTypesIterator.next();
if (c != clazz) {
newList.add(c);
}
}
}
classes = new Class<?>[newList.size()];
newList.toArray(classes);
cache.put(clazz, classes);
// save this snapshot back to the original (single writer principle)
superClassesREF.lazySet(this, cache);
}
return classes;
}
/**
* race conditions will result in DUPLICATE answers, which we don't care if happens
* never returns null
* never resets (class hierarchy never changes during runtime)
*
* https://bugs.openjdk.java.net/browse/JDK-6525802 (fixed this in 2007, so Array.newInstance is just as fast (via intrinsics) new [])
* Cache is in place to keep GC down.
*/
public
Class<?> getArrayClass(final Class<?> c) {
// access a snapshot of the subscriptions (single-writer-principle)
final IdentityMap<Class<?>, Class<?>> cache = cast(arrayREF.get(this));
Class<?> clazz = cache.get(c);
if (clazz == null) {
// messy, but the ONLY way to do it. Array super types are also arrays
final Object[] newInstance = (Object[]) Array.newInstance(c, 0);
clazz = newInstance.getClass();
cache.put(c, clazz);
// save this snapshot back to the original (single writer principle)
arrayREF.lazySet(this, cache);
}
return clazz;
}
/**
* Collect all directly and indirectly related super types (classes and interfaces) of a given class.
*
* @param from The root class to start with
* @return An array of classes, each representing a super type of the root class
*/
public static
Iterator<Class<?>> getSuperTypes(Class<?> from) {
// This must be a 'set' because there can be duplicates, depending on the object hierarchy
final IdentityMap<Class<?>, Boolean> superclasses = new IdentityMap<Class<?>, Boolean>();
collectInterfaces(from, superclasses);
while (!from.equals(Object.class) && !from.isInterface()) {
superclasses.put(from.getSuperclass(), Boolean.TRUE);
from = from.getSuperclass();
collectInterfaces(from, superclasses);
}
return superclasses.keys();
}
private static
void collectInterfaces(Class<?> from, IdentityMap<Class<?>, Boolean> accumulator) {
for (Class<?> intface : from.getInterfaces()) {
accumulator.put(intface, Boolean.TRUE);
collectInterfaces(intface, accumulator);
}
}
/**
* Clears the caches, should only be called on shutdown
*/
public
void shutdown() {
this.arrayCache.clear();
this.superClassesCache.clear();
}
@SuppressWarnings("unchecked")
private static
<T> T cast(Object obj) {
return (T) obj;
}
}

View File

@ -1,50 +0,0 @@
/*
* Copyright 2015 dorkbox, llc
*
* 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.
*/
package dorkbox.util.classes;
public abstract class ClassResolver {
/**
* A helper class to get the call context. It subclasses SecurityManager to make getClassContext() accessible. An instance of
* CallerResolver only needs to be created, not installed as an actual security manager.
*/
private static final class CallerResolver extends SecurityManager {
@Override
protected Class<?>[] getClassContext() {
return super.getClassContext();
}
}
private static final int CALL_CONTEXT_OFFSET = 3; // may need to change if this class is redesigned
private static final CallerResolver CALLER_RESOLVER;
static {
try {
// This can fail if the current SecurityManager does not allow
// RuntimePermission ("createSecurityManager"):
CALLER_RESOLVER = new CallerResolver();
} catch (SecurityException se) {
throw new RuntimeException("ClassLoaderResolver: could not create CallerResolver: " + se);
}
}
/**
* Indexes into the current method call context with a given offset.
*/
public static Class<?> getCallerClass(final int callerOffset) {
return CALLER_RESOLVER.getClassContext()[CALL_CONTEXT_OFFSET + callerOffset];
}
}

View File

@ -1,188 +0,0 @@
/*
* Copyright 2012 Benjamin Diedrichsen
*
* 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.
*
*
* Copyright 2015 dorkbox, llc
*
* 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.
*/
package dorkbox.util.classes;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import dorkbox.collections.IdentityMap;
/**
* @author bennidi
* Date: 2/16/12
* Time: 12:14 PM
* @author dorkbox
* Date: 2/2/15
*/
public final
class ReflectionUtils {
private static final Method[] EMPTY_METHODS = new Method[0];
private
ReflectionUtils() {
}
/**
* Get methods annotated with the specified annotation.
*
* @param target the class that you are looking for the methods on
* @param annotationClass the annotations that define the method you are looking for
* @param <A> the annotation type
*
* @return the array of methods that match the target + annotation
*/
public static
<A extends Annotation> Method[] getMethods(Class<?> target, Class<A> annotationClass) {
ArrayList<Method> methods = new ArrayList<Method>();
getMethods(target, annotationClass, methods);
return methods.toArray(EMPTY_METHODS);
}
private static
<A extends Annotation> void getMethods(Class<?> target, Class<A> annotationClass, ArrayList<Method> methods) {
try {
for (Method method : target.getDeclaredMethods()) {
if (getAnnotation(method, annotationClass) != null) {
methods.add(method);
}
}
} catch (Exception ignored) {
}
// recursively go until root
if (!target.equals(Object.class)) {
getMethods(target.getSuperclass(), annotationClass, methods);
}
}
/**
* Traverses the class hierarchy upwards, starting at the given subclass, looking
* for an override of the given methods -> finds the bottom most override of the given
* method if any exists
*/
public static
Method getOverridingMethod(final Method overridingMethod, final Class<?> subclass) {
Class<?> current = subclass;
while (!current.equals(overridingMethod.getDeclaringClass())) {
try {
return current.getDeclaredMethod(overridingMethod.getName(), overridingMethod.getParameterTypes());
} catch (NoSuchMethodException e) {
current = current.getSuperclass();
}
}
return null;
}
public static
boolean containsOverridingMethod(final Method[] allMethods, final Method methodToCheck) {
final int length = allMethods.length;
Method method;
for (int i = 0; i < length; i++) {
method = allMethods[i];
if (isOverriddenBy(methodToCheck, method)) {
return true;
}
}
return false;
}
/**
* Searches for an Annotation of the given type on the class. Supports meta annotations.
*
* @param from AnnotatedElement (class, method...)
* @param annotationType Annotation class to look for.
* @param <A> Class of annotation type
* @return Annotation instance or null
*/
private static
<A extends Annotation> A getAnnotation(AnnotatedElement from, Class<A> annotationType, IdentityMap<AnnotatedElement, Boolean> visited) {
if (visited.containsKey(from)) {
return null;
}
visited.put(from, Boolean.TRUE);
A ann = from.getAnnotation(annotationType);
if (ann != null) {
return ann;
}
for (Annotation metaAnn : from.getAnnotations()) {
ann = getAnnotation(metaAnn.annotationType(), annotationType, visited);
if (ann != null) {
return ann;
}
}
return null;
}
public static
<A extends Annotation> A getAnnotation(AnnotatedElement from, Class<A> annotationType) {
return getAnnotation(from, annotationType, new IdentityMap<AnnotatedElement, Boolean>());
}
private static
boolean isOverriddenBy(final Method superclassMethod, final Method subclassMethod) {
// if the declaring classes are the same or the subclass method is not defined in the subclass
// hierarchy of the given superclass method or the method names are not the same then
// subclassMethod does not override superclassMethod
if (superclassMethod.getDeclaringClass().equals(subclassMethod.getDeclaringClass()) ||
!superclassMethod.getDeclaringClass().isAssignableFrom(subclassMethod.getDeclaringClass()) ||
!superclassMethod.getName().equals(subclassMethod.getName())) {
return false;
}
final Class<?>[] superClassMethodParameters = superclassMethod.getParameterTypes();
final Class<?>[] subClassMethodParameters = subclassMethod.getParameterTypes();
// method must specify the same number of parameters
//the parameters must occur in the exact same order
for (int i = 0; i < subClassMethodParameters.length; i++) {
if (!superClassMethodParameters[i].equals(subClassMethodParameters[i])) {
return false;
}
}
return true;
}
}

View File

@ -1,17 +0,0 @@
/*
* Copyright 2021 dorkbox, llc
*
* 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.
*/
package dorkbox.util.classes;

View File

@ -1,801 +0,0 @@
/*
* Copyright (c) 2006 Damien Miller <djm@mindrot.org>
*
* GWT modified version.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package dorkbox.util.crypto;
import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
import java.util.Arrays;
/**
* BCrypt implements OpenBSD-style Blowfish password hashing using
* the scheme described in "A Future-Adaptable Password Scheme" by
* Niels Provos and David Mazieres.
* <p>
* This password hashing system tries to thwart off-line password
* cracking using a computationally-intensive hashing algorithm,
* based on Bruce Schneier's Blowfish cipher. The work factor of
* the algorithm is parameterised, so it can be increased as
* computers get faster.
* <p>
* Usage is really simple. To hash a password for the first time,
* call the hashpw method with a random salt, like this:
* <p>
* <code>
* String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt()); <br />
* </code>
* <p>
* To check whether a plaintext password matches one that has been
* hashed previously, use the checkpw method:
* <p>
* <code>
* if (BCrypt.checkpw(candidate_password, stored_hash))<br />
* &nbsp;&nbsp;&nbsp;&nbsp;System.out.println("It matches");<br />
* else<br />
* &nbsp;&nbsp;&nbsp;&nbsp;System.out.println("It does not match");<br />
* </code>
* <p>
* The gensalt() method takes an optional parameter (log_rounds)
* that determines the computational complexity of the hashing:
* <p>
* <code>
* String strong_salt = BCrypt.gensalt(10)<br />
* String stronger_salt = BCrypt.gensalt(12)<br />
* </code>
* <p>
* The amount of work increases exponentially (2**log_rounds), so
* each increment is twice as much work. The default log_rounds is
* 10, and the valid range is 4 to 31.
*
* @author Damien Miller
* @version 0.2
*/
public class BCrypt {
// BCrypt parameters
private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10;
private static final int BCRYPT_SALT_LEN = 16;
// Blowfish parameters
private static final int BLOWFISH_NUM_ROUNDS = 16;
// Initial contents of key schedule
private static final int P_orig[] = {
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
0x9216d5d9, 0x8979fb1b
};
private static final int S_orig[] = {
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
};
// bcrypt IV: "OrpheanBeholderScryDoubt"
static private final int bf_crypt_ciphertext[] = {
0x4f727068, 0x65616e42, 0x65686f6c,
0x64657253, 0x63727944, 0x6f756274
};
// Table for Base64 encoding
static private final char base64_code[] = {
'.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9'
};
// Table for Base64 decoding
static private final byte index_64[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, 0, 1, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63, -1, -1,
-1, -1, -1, -1, -1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
-1, -1, -1, -1, -1, -1, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
51, 52, 53, -1, -1, -1, -1, -1
};
// Expanded Blowfish key
private int P[];
private int S[];
/**
* Encode a byte array using bcrypt's slightly-modified base64
* encoding scheme. Note that this is *not* compatible with
* the standard MIME-base64 encoding.
*
* @param d the byte array to encode
* @param len the number of bytes to encode
* @return base64-encoded string
* @exception IllegalArgumentException if the length is invalid
*/
private static String encode_base64(byte d[], int len)
throws IllegalArgumentException {
int off = 0;
StringBuilder rs = new StringBuilder();
int c1, c2;
if (len <= 0 || len > d.length) {
throw new IllegalArgumentException ("Invalid len");
}
while (off < len) {
c1 = d[off++] & 0xff;
rs.append(base64_code[c1 >> 2 & 0x3f]);
c1 = (c1 & 0x03) << 4;
if (off >= len) {
rs.append(base64_code[c1 & 0x3f]);
break;
}
c2 = d[off++] & 0xff;
c1 |= c2 >> 4 & 0x0f;
rs.append(base64_code[c1 & 0x3f]);
c1 = (c2 & 0x0f) << 2;
if (off >= len) {
rs.append(base64_code[c1 & 0x3f]);
break;
}
c2 = d[off++] & 0xff;
c1 |= c2 >> 6 & 0x03;
rs.append(base64_code[c1 & 0x3f]);
rs.append(base64_code[c2 & 0x3f]);
}
return rs.toString();
}
/**
* Look up the 3 bits base64-encoded by the specified character, range-checking againt conversion table
*
* @param x the base64-encoded value
* @return the decoded value of x
*/
private static byte char64(char x) {
if (x < 0 || x > index_64.length) {
return -1;
}
return index_64[x];
}
/**
* Decode a string encoded using bcrypt's base64 scheme to a byte array. Note that this is *not* compatible with
* the standard MIME-base64 encoding.
*
* @param s the string to decode
* @param maxolen the maximum number of bytes to decode
* @return an array containing the decoded bytes
* @throws IllegalArgumentException if maxolen is invalid
*/
private static byte[] decode_base64(String s, int maxolen)
throws IllegalArgumentException {
StringBuilder rs = new StringBuilder();
int off = 0, slen = s.length(), olen = 0;
byte ret[];
byte c1, c2, c3, c4, o;
if (maxolen <= 0) {
throw new IllegalArgumentException ("Invalid maxolen");
}
while (off < slen - 1 && olen < maxolen) {
c1 = char64(s.charAt(off++));
c2 = char64(s.charAt(off++));
if (c1 == -1 || c2 == -1) {
break;
}
o = (byte)(c1 << 2);
o |= (c2 & 0x30) >> 4;
rs.append((char)o);
if (++olen >= maxolen || off >= slen) {
break;
}
c3 = char64(s.charAt(off++));
if (c3 == -1) {
break;
}
o = (byte)((c2 & 0x0f) << 4);
o |= (c3 & 0x3c) >> 2;
rs.append((char)o);
if (++olen >= maxolen || off >= slen) {
break;
}
c4 = char64(s.charAt(off++));
o = (byte)((c3 & 0x03) << 6);
o |= c4;
rs.append((char)o);
++olen;
}
ret = new byte[olen];
for (off = 0; off < olen; off++) {
ret[off] = (byte)rs.charAt(off);
}
return ret;
}
/**
* Blowfish encipher a single 64-bit block encoded as two 32-bit halves
*
* @param lr an array containing the two 32-bit half blocks
* @param off the position in the array of the blocks
*/
private
void encipher(int lr[], int off) {
int i, n, l = lr[off], r = lr[off + 1];
l ^= P[0];
for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2;) {
// Feistel substitution on left word
n = S[l >> 24 & 0xff];
n += S[0x100 | l >> 16 & 0xff];
n ^= S[0x200 | l >> 8 & 0xff];
n += S[0x300 | l & 0xff];
r ^= n ^ P[++i];
// Feistel substitution on right word
n = S[r >> 24 & 0xff];
n += S[0x100 | r >> 16 & 0xff];
n ^= S[0x200 | r >> 8 & 0xff];
n += S[0x300 | r & 0xff];
l ^= n ^ P[++i];
}
lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1];
lr[off + 1] = l;
}
/**
* Cycically extract a word of key material
*
* @param data the string to extract the data from
* @param offp a "pointer" (as a one-entry array) to the current offset into data
* @return the next word of material from data
*/
private static int streamtoword(byte data[], int offp[]) {
int i;
int word = 0;
int off = offp[0];
for (i = 0; i < 4; i++) {
word = word << 8 | data[off] & 0xff;
off = (off + 1) % data.length;
}
offp[0] = off;
return word;
}
/**
* Initialize the Blowfish key schedule
*/
private void init_key() {
P = Arrays.copyOf(P_orig, P_orig.length);
S = Arrays.copyOf(S_orig, S_orig.length);
}
/**
* Key the Blowfish cipher
* @param key an array containing the key
*/
private void key(byte key[]) {
int i;
int koffp[] = { 0 };
int lr[] = { 0, 0 };
int plen = P.length, slen = S.length;
for (i = 0; i < plen; i++) {
P[i] = P[i] ^ streamtoword(key, koffp);
}
for (i = 0; i < plen; i += 2) {
encipher(lr, 0);
P[i] = lr[0];
P[i + 1] = lr[1];
}
for (i = 0; i < slen; i += 2) {
encipher(lr, 0);
S[i] = lr[0];
S[i + 1] = lr[1];
}
}
/**
* Perform the "enhanced key schedule" step described by Provos and Mazieres in "A Future-Adaptable Password Scheme"
* http://www.openbsd.org/papers/bcrypt-paper.ps
*
* @param data salt information
* @param key password information
*/
private void ekskey(byte data[], byte key[]) {
int i;
int koffp[] = { 0 }, doffp[] = { 0 };
int lr[] = { 0, 0 };
int plen = P.length, slen = S.length;
for (i = 0; i < plen; i++) {
P[i] = P[i] ^ streamtoword(key, koffp);
}
for (i = 0; i < plen; i += 2) {
lr[0] ^= streamtoword(data, doffp);
lr[1] ^= streamtoword(data, doffp);
encipher(lr, 0);
P[i] = lr[0];
P[i + 1] = lr[1];
}
for (i = 0; i < slen; i += 2) {
lr[0] ^= streamtoword(data, doffp);
lr[1] ^= streamtoword(data, doffp);
encipher(lr, 0);
S[i] = lr[0];
S[i + 1] = lr[1];
}
}
/**
* Perform the central password hashing step in the bcrypt scheme
*
* @param password the password to hash
* @param salt the binary salt to hash with the password
* @param log_rounds the binary logarithm of the number of rounds of hashing to apply
* @return an array containing the binary hashed password
*/
private byte[] crypt_raw(byte password[], byte salt[], int log_rounds, int cdata[]) {
int rounds, i, j;
int clen = cdata.length;
byte ret[];
if (log_rounds < 4 || log_rounds > 30) {
throw new IllegalArgumentException ("Bad number of rounds");
}
rounds = 1 << log_rounds;
if (salt.length != BCRYPT_SALT_LEN) {
throw new IllegalArgumentException ("Bad salt length");
}
init_key();
ekskey(salt, password);
for (i = 0; i != rounds; i++) {
key(password);
key(salt);
}
for (i = 0; i < 64; i++) {
for (j = 0; j < clen >> 1; j++) {
encipher(cdata, j << 1);
}
}
ret = new byte[clen * 4];
for (i = 0, j = 0; i < clen; i++) {
ret[j++] = (byte)(cdata[i] >> 24 & 0xff);
ret[j++] = (byte)(cdata[i] >> 16 & 0xff);
ret[j++] = (byte)(cdata[i] >> 8 & 0xff);
ret[j++] = (byte)(cdata[i] & 0xff);
}
return ret;
}
/**
* Hash a password using the OpenBSD bcrypt scheme
*
* @param password the password to hash
* @return the hashed password
*/
public static String hashpw(String password) {
return hashpw(password, BCrypt.gensalt());
}
/**
* Hash a password using the OpenBSD bcrypt scheme
*
* @param password the password to hash
* @param salt the salt to hash with (perhaps generated using BCrypt.gensalt)
* @return the hashed password
*/
public static String hashpw(String password, String salt) {
BCrypt B;
String real_salt;
byte passwordb[], saltb[], hashed[];
char minor = (char)0;
int rounds, off = 0;
StringBuilder rs = new StringBuilder();
if (salt.charAt(0) != '$' || salt.charAt(1) != '2') {
throw new IllegalArgumentException ("Invalid salt version");
}
if (salt.charAt(2) == '$') {
off = 3;
} else {
minor = salt.charAt(2);
if (minor != 'a' || salt.charAt(3) != '$') {
throw new IllegalArgumentException ("Invalid salt revision");
}
off = 4;
}
// Extract number of rounds
if (salt.charAt(off + 2) > '$') {
throw new IllegalArgumentException ("Missing salt rounds");
}
rounds = Integer.parseInt(salt.substring(off, off + 2));
real_salt = salt.substring(off + 3, off + 25);
try {
passwordb = (password + (minor >= 'a' ? "\000" : "")).getBytes("UTF-8");
} catch (UnsupportedEncodingException uee) {
throw new AssertionError("UTF-8 is not supported");
}
saltb = decode_base64(real_salt, BCRYPT_SALT_LEN);
B = new BCrypt();
hashed = B.crypt_raw(passwordb, saltb, rounds, Arrays.copyOf(bf_crypt_ciphertext, bf_crypt_ciphertext.length));
rs.append("$2");
if (minor >= 'a') {
rs.append(minor);
}
rs.append("$");
if (rounds < 10) {
rs.append("0");
}
if (rounds > 30) {
throw new IllegalArgumentException("rounds exceeds maximum (30)");
}
rs.append(rounds);
rs.append("$");
rs.append(encode_base64(saltb, saltb.length));
rs.append(encode_base64(hashed, bf_crypt_ciphertext.length * 4 - 1));
return rs.toString();
}
/**
* Generate a salt for use with the BCrypt.hashpw() method
* @param log_rounds the log2 of the number of rounds of
* hashing to apply - the work factor therefore increases as
* 2**log_rounds.
* @param random an instance of SecureRandom to use
* @return an encoded salt value
*/
public static String gensalt(int log_rounds, SecureRandom random) {
StringBuilder rs = new StringBuilder();
byte rnd[] = new byte[BCRYPT_SALT_LEN];
random.nextBytes(rnd);
rs.append("$2a$");
if (log_rounds < 10) {
rs.append("0");
}
rs.append(String.valueOf(log_rounds));
rs.append("$");
rs.append(encode_base64(rnd, rnd.length));
return rs.toString();
}
/**
* Generate a salt for use with the BCrypt.hashpw() method
* @param log_rounds the log2 of the number of rounds of
* hashing to apply - the work factor therefore increases as
* 2**log_rounds.
* @return an encoded salt value
*/
public static String gensalt(int log_rounds) {
return gensalt(log_rounds, new SecureRandom());
}
/**
* Generate a salt for use with the BCrypt.hashpw() method,
* selecting a reasonable default for the number of hashing
* rounds to apply
* @return an encoded salt value
*/
public static String gensalt() {
return gensalt(GENSALT_DEFAULT_LOG2_ROUNDS);
}
/**
* Check that a plaintext password matches a previously hashed
* one
* @param plaintext the plaintext password to verify
* @param hashed the previously-hashed password
* @return true if the passwords match, false otherwise
*/
public static boolean checkpw(String plaintext, String hashed) {
byte hashed_bytes[];
byte try_bytes[];
try {
String try_pw = hashpw(plaintext, hashed);
hashed_bytes = hashed.getBytes("UTF-8");
try_bytes = try_pw.getBytes("UTF-8");
} catch (UnsupportedEncodingException uee) {
return false;
}
if (hashed_bytes.length != try_bytes.length)
return false;
byte ret = 0;
for (int i = 0; i < try_bytes.length; i++)
ret |= hashed_bytes[i] ^ try_bytes[i];
return ret == 0;
}
}

View File

@ -1,485 +0,0 @@
/*
* Copyright 2010 dorkbox, llc
*
* 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.
*/
package dorkbox.util.crypto;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.Security;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import javax.crypto.Cipher;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.PBEParametersGenerator;
import org.bouncycastle.crypto.digests.MD5Digest;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.digests.SHA256Digest;
import org.bouncycastle.crypto.digests.SHA512Digest;
import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.lwjgl.util.xxhash.XXH32State;
import org.lwjgl.util.xxhash.XXHash;
import org.slf4j.Logger;
/**
* http://en.wikipedia.org/wiki/NSA_Suite_B http://www.nsa.gov/ia/programs/suiteb_cryptography/
* <p/>
* NSA Suite B
* <p/>
* TOP-SECRET LEVEL AES256/GCM ECC with 384-bit prime curve (FIPS PUB 186-3), and SHA-384
* <p/>
* SECRET LEVEL AES 128 ECDH and ECDSA using the 256-bit prime (FIPS PUB 186-3), and SHA-256. RSA with 2048 can be used for DH key
* negotiation
* <p/>
* WARNING! Note that this call is INCOMPATIBLE with GWT, so we have EXCLUDED IT from gwt, and created a CryptoGwt class in the web-client
* project which only has the necessary crypto utility methods that are 1) Necessary 2) Compatible with GWT
* <p/>
* <p/>
* To determine if we have hardware accelerated AES java -XX:+PrintFlagsFinal -version | grep UseAES
*
* Per NIST SP800-38D,
* The total number of invocations of the authenticated encryption function shall not exceed 232, including all IV lengths and all instances of the authenticated encryption function with the given key.
*
*/
public final
class Crypto {
private
Crypto() {
}
// CUSTOM_HEADER USE
// check to see if our extra data is OURS. if so, process it
// cafeʞ, as UN signed bytes is: [254, 202, 202, 158], or as hex: FECA CA9E
// cafeʞ, as signed bytes is: [-2, -54, -54, -98]
private static final byte[] CUSTOM_HEADER = new byte[] {(byte) -2, (byte) -54, (byte) -54, (byte) -98};
public static
void addProvider() {
// make sure we only add it once (in case it's added elsewhere...)
Provider provider = Security.getProvider(BouncyCastleProvider.PROVIDER_NAME);
if (provider == null) {
Security.addProvider(new BouncyCastleProvider());
}
}
/**
* Determines if cryptography restrictions apply.
* Restrictions apply if the value of {@link Cipher#getMaxAllowedKeyLength(String)} returns a value smaller than {@link Integer#MAX_VALUE} if there are any restrictions according to the JavaDoc of the method.
* This method is used with the transform <code>"AES/CBC/PKCS5Padding"</code> as this is an often used algorithm that is <a href="https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#impl">an implementation requirement for Java SE</a>.
*
* @return <code>true</code> if restrictions apply, <code>false</code> otherwise
*/
public static boolean restrictedCryptography() {
try {
return Cipher.getMaxAllowedKeyLength("AES/CBC/PKCS5Padding") < Integer.MAX_VALUE;
} catch (final NoSuchAlgorithmException e) {
throw new IllegalStateException("The transform \"AES/CBC/PKCS5Padding\" is not available (the availability of this algorithm is mandatory for Java SE implementations)", e);
}
}
public static
byte[] hashFileMD5(File file) {
MD5Digest digest = new MD5Digest();
return hashFile(file, digest, null);
}
public static
byte[] hashFileSHA1(File file) {
SHA1Digest digest = new SHA1Digest();
return hashFile(file, digest, null);
}
public static
byte[] hashFileSHA256(File file) {
SHA256Digest digest = new SHA256Digest();
return hashFile(file, digest, null);
}
public static
byte[] hashFileSHA512(File file) {
SHA512Digest digest = new SHA512Digest();
return hashFile(file, digest, null);
}
/**
* Return the hash of the file or NULL if file is invalid
*
* @param logger
* may be null, if no log output is necessary
*/
public static
byte[] hashFile(File file, Digest digest, Logger logger) {
return hashFile(file, digest, 0L, file.length(), logger);
}
/**
* Return the hash of the file or NULL if file is invalid
*
* @param logger
* may be null, if no log output is necessary
*/
public static
byte[] hashFile(File file, Digest digest, long startPosition, long endPosition, Logger logger) {
if (file.isFile() && file.canRead()) {
InputStream inputStream = null;
try {
inputStream = new FileInputStream(file);
long skip = inputStream.skip(startPosition);
if (skip != startPosition) {
throw new RuntimeException("Unable to skip " + startPosition + " bytes. Only skippped " + skip + " instead");
}
long size = file.length() - startPosition;
long lengthFromEnd = size - endPosition;
if (lengthFromEnd > 0 && lengthFromEnd < size) {
size -= lengthFromEnd;
}
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
int readBytes;
digest.reset();
while (size > 0) {
//noinspection NumericCastThatLosesPrecision
int maxToRead = (int) Math.min(bufferSize, size);
readBytes = inputStream.read(buffer, 0, maxToRead);
size -= readBytes;
if (readBytes == 0) {
//wtf. finally still gets called.
return null;
}
digest.update(buffer, 0, readBytes);
}
} catch (Exception e) {
if (logger != null) {
logger.error("Error hashing file: {}", file.getAbsolutePath(), e);
} else {
e.printStackTrace();
}
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
byte[] digestBytes = new byte[digest.getDigestSize()];
digest.doFinal(digestBytes, 0);
return digestBytes;
}
else {
return null;
}
}
/**
* Return the xxhash of the file as or 0 if file is invalid
*
* @param logger
* may be null, if no log output is necessary
*/
public static
int xxHashFile(File file, long lengthFromEnd, Logger logger) {
if (file.isFile() && file.canRead()) {
InputStream inputStream = null;
// used to initialize the hash value, use whatever value you want, but always the same
int seed = 0x9747b28c; // must match number in C (in Auth::xxHash32())
XXH32State state = XXHash.XXH32_createState();
XXHash.XXH32_reset(state, seed);
try {
inputStream = new FileInputStream(file);
long size = file.length();
if (lengthFromEnd > 0 && lengthFromEnd < size) {
size -= lengthFromEnd;
}
int bufferSize = 4096;
byte[] buffer = new byte[bufferSize];
ByteBuffer bbuffer = ByteBuffer.wrap(buffer);
int readBytes;
while (size > 0) {
//noinspection NumericCastThatLosesPrecision
int maxToRead = (int) Math.min(bufferSize, size);
readBytes = inputStream.read(buffer, 0, maxToRead);
size -= readBytes;
if (readBytes == 0) {
//wtf. finally still gets called.
return 0;
}
bbuffer.limit(readBytes);
XXHash.XXH32_update(state, bbuffer);
}
} catch (Exception e) {
if (logger != null) {
logger.error("Error hashing file: {}", file.getAbsolutePath(), e);
} else {
e.printStackTrace();
}
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return XXHash.XXH32_digest(state);
}
else {
return 0;
}
}
static
int toInt(final byte[] bytes) {
int number = 0;
switch (bytes.length) {
default:
case 4:
number |= (bytes[3] & 0xFF) << 24;
case 3:
number |= (bytes[2] & 0xFF) << 16;
case 2:
number |= (bytes[1] & 0xFF) << 8;
case 1:
number |= (bytes[0] & 0xFF) << 0;
}
return number;
}
/**
* Specifically, to return the hash of the ALL files/directories inside the jar, minus the action specified (LGPL) files.
*/
public static
byte[] hashJarContentsExcludeAction(File jarDestFilename, Digest digest, int action) throws IOException {
JarFile jarDestFile = new JarFile(jarDestFilename);
try {
Enumeration<JarEntry> jarElements = jarDestFile.entries();
boolean okToHash;
boolean hasAction;
byte[] buffer = new byte[2048];
int read;
digest.reset();
while (jarElements.hasMoreElements()) {
JarEntry jarEntry = jarElements.nextElement();
String name = jarEntry.getName();
okToHash = !jarEntry.isDirectory();
if (!okToHash) {
continue;
}
// data with NO extra data will NOT BE HASHED
// data that matches our action bitmask WILL NOT BE HASHED
okToHash = false;
hasAction = false;
byte[] extraData = jarEntry.getExtra();
if (extraData == null || extraData.length == 0) {
okToHash = false;
}
else if (extraData.length >= 4) {
for (int i = 0; i < CUSTOM_HEADER.length; i++) {
if (extraData[i] != CUSTOM_HEADER[i]) {
throw new RuntimeException("Unexpected extra data in zip assigned. Aborting");
}
}
// this means we matched our header
if (extraData[4] > 0) {
hasAction = true;
// we have an ACTION describing how it was compressed, etc
int fileAction = toInt(new byte[] {extraData[5], extraData[6], extraData[7], extraData[8]});
if ((fileAction & action) != action) {
okToHash = true;
}
}
else {
okToHash = true;
}
}
else {
throw new RuntimeException("Unexpected extra data in zip assigned. Aborting");
}
// skips hashing lgpl files. (technically, whatever our action bitmask is...)
// we want to hash everything BY DEFAULT. we ALSO want to hash the NAME, LOAD ACTION TYPE, and the contents
if (okToHash) {
// System.err.println("HASHING: " + name);
// hash the file name
byte[] bytes = name.getBytes(StandardCharsets.US_ASCII);
digest.update(bytes, 0, bytes.length);
if (hasAction) {
// hash the action - since we don't want to permit anyone to change this after we sign the file
digest.update(extraData, 5, 4);
}
// hash the contents
InputStream inputStream = jarDestFile.getInputStream(jarEntry);
while ((read = inputStream.read(buffer)) > 0) {
digest.update(buffer, 0, read);
}
inputStream.close();
}
//else {
// System.err.println("Skipping: " + name);
//}
}
} catch (Exception e) {
throw new RuntimeException("Unexpected extra data in zip assigned. Aborting");
} finally {
jarDestFile.close();
}
byte[] digestBytes = new byte[digest.getDigestSize()];
digest.doFinal(digestBytes, 0);
return digestBytes;
}
/**
* Hash an input stream, based on the specified digest
*/
public static
byte[] hashStream(Digest digest, InputStream inputStream) throws IOException {
byte[] buffer = new byte[2048];
int read;
digest.reset();
while ((read = inputStream.read(buffer)) > 0) {
digest.update(buffer, 0, read);
}
inputStream.close();
byte[] digestBytes = new byte[digest.getDigestSize()];
digest.doFinal(digestBytes, 0);
return digestBytes;
}
/**
* Secure way to generate an AES key based on a password. Will '*' out the passed-in password
*
* @param password
* will be filled with '*'
* @param salt
* should be a RANDOM number, at least 256bits (32 bytes) in size.
* @param iterationCount
* should be a lot, like 10,000
*
* @return the secure key to use
*/
public static
byte[] PBKDF2(char[] password, byte[] salt, int iterationCount) {
// will also zero out the password.
byte[] charToBytes = Crypto.charToBytesPassword_UTF16(password);
return PBKDF2(charToBytes, salt, iterationCount);
}
/**
* Secure way to generate an AES key based on a password.
*
* @param password
* The password that you want to mix
* @param salt
* should be a RANDOM number, at least 256bits (32 bytes) in size.
* @param iterationCount
* should be a lot, like 10,000
*
* @return the secure key to use
*/
public static
byte[] PBKDF2(byte[] password, byte[] salt, int iterationCount) {
SHA256Digest digest = new SHA256Digest();
PBEParametersGenerator pGen = new PKCS5S2ParametersGenerator(digest);
pGen.init(password, salt, iterationCount);
KeyParameter key = (KeyParameter) pGen.generateDerivedMacParameters(digest.getDigestSize() * 8); // *8 for bit length.
// zero out the password.
Arrays.fill(password, (byte) 0);
return key.getKey();
}
/**
* this saves the char array in UTF-16 format of bytes and BLANKS out the password char array.
*/
public static
byte[] charToBytesPassword_UTF16(char[] password) {
// note: this saves the char array in UTF-16 format of bytes.
byte[] passwordBytes = new byte[password.length * 2];
for (int i = 0; i < password.length; i++) {
//noinspection NumericCastThatLosesPrecision
passwordBytes[2 * i] = (byte) (((int) password[i] & 0xFF00) >> 8);
//noinspection NumericCastThatLosesPrecision
passwordBytes[2 * i + 1] = (byte) ((int) password[i] & 0x00FF);
}
// asterisk out the password
Arrays.fill(password, '*');
return passwordBytes;
}
}

View File

@ -1,625 +0,0 @@
/*
* Copyright 2010 dorkbox, llc
*
* 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.
*/
package dorkbox.util.crypto;
import org.bouncycastle.crypto.BufferedBlockCipher;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
import org.slf4j.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* AES crypto functions
*/
@SuppressWarnings({"Duplicates"})
public final
class CryptoAES {
private static final int ivSize = 16;
/**
* AES encrypts data with a specified key.
*
* @param aesIV
* must be a nonce (unique value) !!
* @param logger
* may be null, if no log output is necessary
*
* @return empty byte[] if error
*/
public static
byte[] encryptWithIV(GCMBlockCipher aesEngine, byte[] aesKey, byte[] aesIV, byte[] data, Logger logger) {
byte[] encryptAES = encrypt(aesEngine, aesKey, aesIV, data, logger);
int length = encryptAES.length;
byte[] out = new byte[length + ivSize];
System.arraycopy(aesIV, 0, out, 0, ivSize);
System.arraycopy(encryptAES, 0, out, ivSize, length);
return out;
}
/**
* <b>CONVENIENCE METHOD ONLY - DO NOT USE UNLESS YOU HAVE TO</b>
* <p>
* Use GCM instead, as it's an authenticated cipher (and "regular" AES is not). This prevents tampering with the blocks of encrypted
* data.
* <p>
* AES encrypts data with a specified key.
*
* @param aesIV
* must be a nonce (unique value) !!
* @param logger
* may be null, if no log output is necessary
*
* @return empty byte[] if error
*/
@Deprecated
public static
byte[] encryptWithIV(BufferedBlockCipher aesEngine, byte[] aesKey, byte[] aesIV, byte[] data, Logger logger) {
byte[] encryptAES = encrypt(aesEngine, aesKey, aesIV, data, logger);
int length = encryptAES.length;
byte[] out = new byte[length + ivSize];
System.arraycopy(aesIV, 0, out, 0, ivSize);
System.arraycopy(encryptAES, 0, out, ivSize, length);
return out;
}
/**
* AES encrypts data with a specified key.
*
* @param aesIV
* must be a nonce (unique value) !!
* @param logger
* may be null, if no log output is necessary
*
* @return true if successful
*/
public static
boolean encryptStreamWithIV(GCMBlockCipher aesEngine, byte[] aesKey, byte[] aesIV, InputStream in, OutputStream out, Logger logger) {
try {
out.write(aesIV);
} catch (IOException e) {
if (logger != null) {
logger.error("Unable to perform AES cipher.", e);
}
return false;
}
return encryptStream(aesEngine, aesKey, aesIV, in, out, logger);
}
/**
* <b>CONVENIENCE METHOD ONLY - DO NOT USE UNLESS YOU HAVE TO</b>
* <p>
* Use GCM instead, as it's an authenticated cipher (and "regular" AES is not). This prevents tampering with the blocks of encrypted
* data.
* <p>
* AES encrypts data with a specified key.
*
* @param aesIV
* must be a nonce (unique value) !!
* @param logger
* may be null, if no log output is necessary
*
* @return true if successful
*/
@Deprecated
public static
boolean encryptStreamWithIV(BufferedBlockCipher aesEngine,
byte[] aesKey,
byte[] aesIV,
InputStream in,
OutputStream out,
Logger logger) {
try {
out.write(aesIV);
} catch (IOException e) {
if (logger != null) {
logger.error("Unable to perform AES cipher.", e);
}
return false;
}
return encryptStream(aesEngine, aesKey, aesIV, in, out, logger);
}
/**
* AES encrypts data with a specified key.
*
* @param aesIV
* must be a nonce (unique value) !!
* @param logger
* may be null, if no log output is necessary
*
* @return empty byte[] if error
*/
public static
byte[] encrypt(GCMBlockCipher aesEngine, byte[] aesKey, byte[] aesIV, byte[] data, Logger logger) {
int length = data.length;
CipherParameters aesIVAndKey = new ParametersWithIV(new KeyParameter(aesKey), aesIV);
return encrypt(aesEngine, aesIVAndKey, data, length, logger);
}
/**
* AES encrypts data with a specified key.
*
* @param logger
* may be null, if no log output is necessary
*
* @return length of encrypted data, -1 if there was an error.
*/
public static
byte[] encrypt(GCMBlockCipher aesEngine, CipherParameters aesIVAndKey, byte[] data, int length, Logger logger) {
aesEngine.reset();
aesEngine.init(true, aesIVAndKey);
int minSize = aesEngine.getOutputSize(length);
byte[] outArray = new byte[minSize];
int actualLength = aesEngine.processBytes(data, 0, length, outArray, 0);
try {
actualLength += aesEngine.doFinal(outArray, actualLength);
} catch (Exception e) {
if (logger != null) {
logger.error("Unable to perform AES cipher.", e);
}
return new byte[0];
}
if (outArray.length == actualLength) {
return outArray;
}
else {
byte[] result = new byte[actualLength];
System.arraycopy(outArray, 0, result, 0, result.length);
return result;
}
}
/**
* <b>CONVENIENCE METHOD ONLY - DO NOT USE UNLESS YOU HAVE TO</b>
* <p>
* Use GCM instead, as it's an authenticated cipher (and "regular" AES is not). This prevents tampering with the blocks of encrypted
* data.
* <p>
* AES encrypts data with a specified key.
*
* @param aesIV
* must be a nonce (unique value) !!
* @param logger
* may be null, if no log output is necessary
*
* @return empty byte[] if error
*/
@Deprecated
public static
byte[] encrypt(BufferedBlockCipher aesEngine, byte[] aesKey, byte[] aesIV, byte[] data, Logger logger) {
int length = data.length;
CipherParameters aesIVAndKey = new ParametersWithIV(new KeyParameter(aesKey), aesIV);
aesEngine.reset();
aesEngine.init(true, aesIVAndKey);
int minSize = aesEngine.getOutputSize(length);
byte[] outBuf = new byte[minSize];
int actualLength = aesEngine.processBytes(data, 0, length, outBuf, 0);
try {
actualLength += aesEngine.doFinal(outBuf, actualLength);
} catch (Exception e) {
if (logger != null) {
logger.error("Unable to perform AES cipher.", e);
}
return new byte[0];
}
if (outBuf.length == actualLength) {
return outBuf;
}
else {
byte[] result = new byte[actualLength];
System.arraycopy(outBuf, 0, result, 0, result.length);
return result;
}
}
/**
* <b>CONVENIENCE METHOD ONLY - DO NOT USE UNLESS YOU HAVE TO</b>
* <p>
* Use GCM instead, as it's an authenticated cipher (and "regular" AES is not). This prevents tampering with the blocks of encrypted
* data.
* <p>
* AES encrypt from one stream to another.
*
* @param aesIV
* must be a nonce (unique value) !!
* @param logger
* may be null, if no log output is necessary
*
* @return true if successful
*/
@Deprecated
public static
boolean encryptStream(BufferedBlockCipher aesEngine, byte[] aesKey, byte[] aesIV, InputStream in, OutputStream out, Logger logger) {
byte[] buf = new byte[ivSize];
byte[] outbuf = new byte[512];
CipherParameters aesIVAndKey = new ParametersWithIV(new KeyParameter(aesKey), aesIV);
aesEngine.reset();
aesEngine.init(true, aesIVAndKey);
try {
int bytesRead;
int bytesProcessed;
while ((bytesRead = in.read(buf)) >= 0) {
bytesProcessed = aesEngine.processBytes(buf, 0, bytesRead, outbuf, 0);
out.write(outbuf, 0, bytesProcessed);
}
bytesProcessed = aesEngine.doFinal(outbuf, 0);
out.write(outbuf, 0, bytesProcessed);
out.flush();
} catch (Exception e) {
if (logger != null) {
logger.error("Unable to perform AES cipher.", e);
}
return false;
}
return true;
}
/**
* AES encrypt from one stream to another.
*
* @param logger
* may be null, if no log output is necessary
* @param aesIV
* must be a nonce (unique value) !!
*
* @return true if successful
*/
public static
boolean encryptStream(GCMBlockCipher aesEngine, byte[] aesKey, byte[] aesIV, InputStream in, OutputStream out, Logger logger) {
byte[] buf = new byte[ivSize];
byte[] outbuf = new byte[512];
CipherParameters aesIVAndKey = new ParametersWithIV(new KeyParameter(aesKey), aesIV);
aesEngine.reset();
aesEngine.init(true, aesIVAndKey);
try {
int bytesRead;
int bytesProcessed;
while ((bytesRead = in.read(buf)) >= 0) {
bytesProcessed = aesEngine.processBytes(buf, 0, bytesRead, outbuf, 0);
out.write(outbuf, 0, bytesProcessed);
}
bytesProcessed = aesEngine.doFinal(outbuf, 0);
out.write(outbuf, 0, bytesProcessed);
out.flush();
} catch (Exception e) {
if (logger != null) {
logger.error("Unable to perform AES cipher.", e);
}
return false;
}
return true;
}
/**
* AES decrypt (if the aes IV is included in the data). IV must be a nonce (unique value) !!
*
* @param logger
* may be null, if no log output is necessary
*
* @return empty byte[] if error
*/
public static
byte[] decryptWithIV(GCMBlockCipher aesEngine, byte[] aesKey, byte[] data, Logger logger) {
byte[] aesIV = new byte[ivSize];
System.arraycopy(data, 0, aesIV, 0, ivSize);
byte[] in = new byte[data.length - ivSize];
System.arraycopy(data, ivSize, in, 0, in.length);
return decrypt(aesEngine, aesKey, aesIV, in, logger);
}
/**
* <b>CONVENIENCE METHOD ONLY - DO NOT USE UNLESS YOU HAVE TO</b>
* <p>
* Use GCM instead, as it's an authenticated cipher (and "regular" AES is not). This prevents tampering with the blocks of encrypted
* data.
* <p>
* AES decrypt (if the aes IV is included in the data). IV must be a nonce (unique value)
*
* @param logger
* may be null, if no log output is necessary
*
* @return empty byte[] if error
*/
@Deprecated
public static
byte[] decryptWithIV(BufferedBlockCipher aesEngine, byte[] aesKey, byte[] data, Logger logger) {
byte[] aesIV = new byte[ivSize];
System.arraycopy(data, 0, aesIV, 0, ivSize);
byte[] in = new byte[data.length - ivSize];
System.arraycopy(data, ivSize, in, 0, in.length);
return decrypt(aesEngine, aesKey, aesIV, in, logger);
}
/**
* AES decrypt (if the aes IV is included in the data. IV must be a nonce (unique value)
*
* @param logger
* may be null, if no log output is necessary
*
* @return true if successful
*/
public static
boolean decryptStreamWithIV(GCMBlockCipher aesEngine, byte[] aesKey, InputStream in, OutputStream out, Logger logger) {
byte[] aesIV = new byte[ivSize];
try {
in.read(aesIV, 0, ivSize);
} catch (Exception e) {
if (logger != null) {
logger.error("Unable to perform AES cipher.", e);
}
return false;
}
return decryptStream(aesEngine, aesKey, aesIV, in, out, logger);
}
/**
* <b>CONVENIENCE METHOD ONLY - DO NOT USE UNLESS YOU HAVE TO</b>
* <p>
* Use GCM instead, as it's an authenticated cipher (and "regular" AES is not). This prevents tampering with the blocks of encrypted
* data.
* <p>
* AES decrypt (if the aes IV is included in the data). IV must be a nonce (unique value)
*
* @param logger
* may be null, if no log output is necessary
*
* @return true if successful
*/
@Deprecated
public static
boolean decryptStreamWithIV(BufferedBlockCipher aesEngine, byte[] aesKey, InputStream in, OutputStream out, Logger logger) {
byte[] aesIV = new byte[ivSize];
try {
in.read(aesIV, 0, ivSize);
} catch (Exception e) {
if (logger != null) {
logger.error("Unable to perform AES cipher.", e);
}
return false;
}
return decryptStream(aesEngine, aesKey, aesIV, in, out, logger);
}
/**
* AES decrypt (if we already know the aes IV -- and it's NOT included in the data)
*
* @param aesIV
* must be a nonce (unique value) !!
* @param logger
* may be null, if no log output is necessary
*
* @return empty byte[] if error
*/
public static
byte[] decrypt(GCMBlockCipher aesEngine, byte[] aesKey, byte[] aesIV, byte[] data, Logger logger) {
int length = data.length;
CipherParameters aesIVAndKey = new ParametersWithIV(new KeyParameter(aesKey), aesIV);
aesEngine.reset();
aesEngine.init(false, aesIVAndKey);
int minSize = aesEngine.getOutputSize(length);
byte[] outBuf = new byte[minSize];
int actualLength = aesEngine.processBytes(data, 0, length, outBuf, 0);
try {
actualLength += aesEngine.doFinal(outBuf, actualLength);
} catch (Exception e) {
if (logger != null) {
logger.debug("Unable to perform AES cipher.", e);
}
return new byte[0];
}
if (outBuf.length == actualLength) {
return outBuf;
}
else {
byte[] result = new byte[actualLength];
System.arraycopy(outBuf, 0, result, 0, result.length);
return result;
}
}
/**
* <b>CONVENIENCE METHOD ONLY - DO NOT USE UNLESS YOU HAVE TO</b>
* <p>
* Use GCM instead, as it's an authenticated cipher (and "regular" AES is not). This prevents tampering with the blocks of encrypted
* data.
* <p>
* AES decrypt (if we already know the aes IV -- and it's NOT included in the data)
*
* @param aesIV
* must be a nonce (unique value) !!
* @param logger
* may be null, if no log output is necessary
*
* @return empty byte[] if error
*/
@Deprecated
public static
byte[] decrypt(BufferedBlockCipher aesEngine, byte[] aesKey, byte[] aesIV, byte[] data, Logger logger) {
int length = data.length;
CipherParameters aesIVAndKey = new ParametersWithIV(new KeyParameter(aesKey), aesIV);
aesEngine.reset();
aesEngine.init(false, aesIVAndKey);
int minSize = aesEngine.getOutputSize(length);
byte[] outBuf = new byte[minSize];
int actualLength = aesEngine.processBytes(data, 0, length, outBuf, 0);
try {
actualLength += aesEngine.doFinal(outBuf, actualLength);
} catch (Exception e) {
if (logger != null) {
logger.error("Unable to perform AES cipher.", e);
}
return new byte[0];
}
if (outBuf.length == actualLength) {
return outBuf;
}
else {
byte[] result = new byte[actualLength];
System.arraycopy(outBuf, 0, result, 0, result.length);
return result;
}
}
/**
* AES decrypt from one stream to another.
*
* @param aesIV
* must be a nonce (unique value) !!
* @param logger
* may be null, if no log output is necessary
*
* @return true if successful
*/
public static
boolean decryptStream(GCMBlockCipher aesEngine, byte[] aesKey, byte[] aesIV, InputStream in, OutputStream out, Logger logger) {
byte[] buf = new byte[ivSize];
byte[] outbuf = new byte[512];
CipherParameters aesIVAndKey = new ParametersWithIV(new KeyParameter(aesKey), aesIV);
aesEngine.reset();
aesEngine.init(false, aesIVAndKey);
try {
int bytesRead;
int bytesProcessed;
while ((bytesRead = in.read(buf)) >= 0) {
bytesProcessed = aesEngine.processBytes(buf, 0, bytesRead, outbuf, 0);
out.write(outbuf, 0, bytesProcessed);
}
bytesProcessed = aesEngine.doFinal(outbuf, 0);
out.write(outbuf, 0, bytesProcessed);
out.flush();
} catch (Exception e) {
if (logger != null) {
logger.error("Unable to perform AES cipher.", e);
}
return false;
}
return true;
}
/**
* <b>CONVENIENCE METHOD ONLY - DO NOT USE UNLESS YOU HAVE TO</b>
* <p>
* Use GCM instead, as it's an authenticated cipher (and "regular" AES is not). This prevents tampering with the blocks of encrypted
* data.
* <p>
* AES decrypt from one stream to another.
*
* @param aesIV
* must be a nonce (unique value) !!
* @param logger
* may be null, if no log output is necessary
*
* @return true if successful
*/
@Deprecated
public static
boolean decryptStream(BufferedBlockCipher aesEngine, byte[] aesKey, byte[] aesIV, InputStream in, OutputStream out, Logger logger) {
byte[] buf = new byte[ivSize];
byte[] outbuf = new byte[512];
CipherParameters aesIVAndKey = new ParametersWithIV(new KeyParameter(aesKey), aesIV);
aesEngine.reset();
aesEngine.init(false, aesIVAndKey);
try {
int bytesRead;
int bytesProcessed;
while ((bytesRead = in.read(buf)) >= 0) {
bytesProcessed = aesEngine.processBytes(buf, 0, bytesRead, outbuf, 0);
out.write(outbuf, 0, bytesProcessed);
}
bytesProcessed = aesEngine.doFinal(outbuf, 0);
out.write(outbuf, 0, bytesProcessed);
out.flush();
} catch (Exception e) {
if (logger != null) {
logger.error("Unable to perform AES cipher.", e);
}
return false;
}
return true;
}
private
CryptoAES() {
}
}

View File

@ -1,112 +0,0 @@
/*
* Copyright 2010 dorkbox, llc
*
* 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.
*/
package dorkbox.util.crypto;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.generators.DSAKeyPairGenerator;
import org.bouncycastle.crypto.generators.DSAParametersGenerator;
import org.bouncycastle.crypto.params.DSAKeyGenerationParameters;
import org.bouncycastle.crypto.params.DSAParameters;
import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.signers.DSASigner;
import java.math.BigInteger;
import java.security.SecureRandom;
/**
* this is here just for keeping track of how this is done. This should correct and working, but should NOT be used, and instead use ECC
* crypto.
*/
@Deprecated
public final
class CryptoDSA {
/**
* Generates the DSA key (using RSA and SHA1)
* <p/>
* Note: this is here just for keeping track of how this is done. This should NOT be used, and instead use ECC crypto.
*/
public static
AsymmetricCipherKeyPair generateKeyPair(SecureRandom secureRandom, int keyLength) {
DSAKeyPairGenerator keyGen = new DSAKeyPairGenerator();
DSAParametersGenerator dsaParametersGenerator = new DSAParametersGenerator();
dsaParametersGenerator.init(keyLength, 20, secureRandom);
DSAParameters generateParameters = dsaParametersGenerator.generateParameters();
DSAKeyGenerationParameters params = new DSAKeyGenerationParameters(secureRandom, generateParameters);
keyGen.init(params);
return keyGen.generateKeyPair();
}
/**
* The message will have the SHA1 hash calculated and used for the signature.
* <p/>
* Note: this is here just for keeping track of how this is done. This should NOT be used, and instead use ECC crypto.
* <p/>
* The returned signature is the {r,s} signature array.
*/
public static
BigInteger[] generateSignature(DSAPrivateKeyParameters privateKey, SecureRandom secureRandom, byte[] message) {
ParametersWithRandom param = new ParametersWithRandom(privateKey, secureRandom);
DSASigner dsa = new DSASigner();
dsa.init(true, param);
SHA1Digest sha1Digest = new SHA1Digest();
byte[] checksum = new byte[sha1Digest.getDigestSize()];
sha1Digest.update(message, 0, message.length);
sha1Digest.doFinal(checksum, 0);
return dsa.generateSignature(checksum);
}
/**
* The message will have the SHA1 hash calculated and used for the signature.
* <p/>
* Note: this is here just for keeping track of how this is done. This should NOT be used, and instead use ECC crypto.
*
* @param signature
* is the {r,s} signature array.
*
* @return true if the signature is valid
*/
public static
boolean verifySignature(DSAPublicKeyParameters publicKey, byte[] message, BigInteger[] signature) {
SHA1Digest sha1Digest = new SHA1Digest();
byte[] checksum = new byte[sha1Digest.getDigestSize()];
sha1Digest.update(message, 0, message.length);
sha1Digest.doFinal(checksum, 0);
DSASigner dsa = new DSASigner();
dsa.init(false, publicKey);
return dsa.verifySignature(checksum, signature[0], signature[1]);
}
private
CryptoDSA() {
}
}

View File

@ -1,409 +0,0 @@
/*
* Copyright 2010 dorkbox, llc
*
* 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.
*/
package dorkbox.util.crypto;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Arrays;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.agreement.ECDHCBasicAgreement;
import org.bouncycastle.crypto.digests.SHA384Digest;
import org.bouncycastle.crypto.digests.SHA512Digest;
import org.bouncycastle.crypto.engines.IESEngine;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.generators.KDF2BytesGenerator;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.IESParameters;
import org.bouncycastle.crypto.params.IESWithCipherParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.signers.ECDSASigner;
import org.bouncycastle.jcajce.provider.util.DigestFactory;
import org.bouncycastle.jce.ECNamedCurveTable;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECPoint;
import org.slf4j.Logger;
/**
* ECC crypto functions
*/
public final
class CryptoECC {
public static final String p521_curve = "secp521r1";
public static final String curve25519 = "curve25519";
public static final String default_curve = curve25519;
public static final int macSize = 512;
// on NIST vs 25519 vs Brainpool, see:
// - http://ogryb.blogspot.de/2014/11/why-i-dont-trust-nist-p-256.html
// - http://credelius.com/credelius/?p=97
// - http://safecurves.cr.yp.to/
// we should be using 25519, because NIST and brainpool are "unsafe". Brainpool is "more random" than 25519, but is still not considered safe.
// more info about ECC from:
// http://www.johannes-bauer.com/compsci/ecc/?menuid=4
// http://stackoverflow.com/questions/7419183/problems-implementing-ecdh-on-android-using-bouncycastle
// http://tools.ietf.org/html/draft-jivsov-openpgp-ecc-06#page-4
// http://www.nsa.gov/ia/programs/suiteb_cryptography/
// https://github.com/nelenkov/ecdh-kx/blob/master/src/org/nick/ecdhkx/Crypto.java
// http://nelenkov.blogspot.com/2011/12/using-ecdh-on-android.html
// http://www.secg.org/collateral/sec1_final.pdf
/**
* Uses SHA512
*/
public static
IESEngine createEngine() {
return new IESEngine(new ECDHCBasicAgreement(), new KDF2BytesGenerator(new SHA384Digest()), new HMac(new SHA512Digest()));
}
/**
* Uses SHA512
*/
public static
IESEngine createEngine(PaddedBufferedBlockCipher aesEngine) {
return new IESEngine(new ECDHCBasicAgreement(),
new KDF2BytesGenerator(new SHA384Digest()),
new HMac(new SHA512Digest()),
aesEngine);
}
/**
* These parameters are shared between the two parties. These are a NONCE (use ONCE number!!)
*/
public static
IESParameters generateSharedParameters(SecureRandom secureRandom) {
int macSize = CryptoECC.macSize; // must be the MAC size
// MUST be random EACH TIME encrypt/sign happens!
byte[] derivation = new byte[macSize / 8];
byte[] encoding = new byte[macSize / 8];
secureRandom.nextBytes(derivation);
secureRandom.nextBytes(encoding);
return new IESParameters(derivation, encoding, macSize);
}
/**
* AES-256 ONLY!
*/
public static
IESWithCipherParameters generateSharedParametersWithCipher(SecureRandom secureRandom) {
int macSize = CryptoECC.macSize; // must be the MAC size
byte[] derivation = new byte[macSize / 8]; // MUST be random EACH TIME encrypt/sign happens!
byte[] encoding = new byte[macSize / 8];
secureRandom.nextBytes(derivation);
secureRandom.nextBytes(encoding);
return new IESWithCipherParameters(derivation, encoding, macSize, 256);
}
public static
AsymmetricCipherKeyPair generateKeyPair(String eccCurveName, SecureRandom secureRandom) {
ECParameterSpec eccSpec = ECNamedCurveTable.getParameterSpec(eccCurveName);
return generateKeyPair(eccSpec, secureRandom);
}
public static
AsymmetricCipherKeyPair generateKeyPair(ECParameterSpec eccSpec, SecureRandom secureRandom) {
ECKeyGenerationParameters ecParams = new ECKeyGenerationParameters(new ECDomainParameters(eccSpec.getCurve(),
eccSpec.getG(),
eccSpec.getN()), secureRandom);
ECKeyPairGenerator ecKeyGen = new ECKeyPairGenerator();
ecKeyGen.init(ecParams);
return ecKeyGen.generateKeyPair();
}
/**
* ECC encrypts data with a specified key.
*
* @param logger
* may be null, if no log output is necessary
*
* @return empty byte[] if error
*/
public static
byte[] encrypt(IESEngine eccEngine,
CipherParameters private1,
CipherParameters public2,
IESParameters cipherParams,
byte[] message,
Logger logger) {
eccEngine.init(true, private1, public2, cipherParams);
//noinspection Duplicates
try {
return eccEngine.processBlock(message, 0, message.length);
} catch (Exception e) {
if (logger != null) {
logger.error("Unable to perform ECC cipher.", e);
}
return new byte[0];
}
}
/**
* ECC decrypt data with a specified key.
*
* @param logger
* may be null, if no log output is necessary
*
* @return empty byte[] if error
*/
public static
byte[] decrypt(IESEngine eccEngine,
CipherParameters private2,
CipherParameters public1,
IESParameters cipherParams,
byte[] encrypted,
Logger logger) {
eccEngine.init(false, private2, public1, cipherParams);
//noinspection Duplicates
try {
return eccEngine.processBlock(encrypted, 0, encrypted.length);
} catch (Exception e) {
if (logger != null) {
logger.error("Unable to perform ECC cipher.", e);
}
return new byte[0];
}
}
public static
boolean compare(ECPrivateKeyParameters privateA, ECPrivateKeyParameters privateB) {
ECDomainParameters parametersA = privateA.getParameters();
ECDomainParameters parametersB = privateB.getParameters();
// is it the same curve?
boolean equals = parametersA.getCurve()
.equals(parametersB.getCurve());
if (!equals) {
return false;
}
equals = parametersA.getG()
.equals(parametersB.getG());
if (!equals) {
return false;
}
equals = parametersA.getH()
.equals(parametersB.getH());
if (!equals) {
return false;
}
equals = parametersA.getN()
.equals(parametersB.getN());
if (!equals) {
return false;
}
equals = privateA.getD()
.equals(privateB.getD());
return equals;
}
/**
* @return true if publicA and publicB are NOT NULL, and are both equal to eachother
*/
@SuppressWarnings({"RedundantIfStatement", "SpellCheckingInspection"})
public static
boolean compare(ECPublicKeyParameters publicA, ECPublicKeyParameters publicB) {
if (publicA == null || publicB == null) {
return false;
}
ECDomainParameters parametersA = publicA.getParameters();
ECDomainParameters parametersB = publicB.getParameters();
// is it the same curve?
boolean equals = parametersA.getCurve()
.equals(parametersB.getCurve());
if (!equals) {
return false;
}
equals = parametersA.getG()
.equals(parametersB.getG());
if (!equals) {
return false;
}
equals = parametersA.getH()
.equals(parametersB.getH());
if (!equals) {
return false;
}
equals = parametersA.getN()
.equals(parametersB.getN());
if (!equals) {
return false;
}
ECPoint normalizeA = publicA.getQ()
.normalize();
ECPoint normalizeB = publicB.getQ()
.normalize();
ECFieldElement xCoordA = normalizeA.getXCoord();
ECFieldElement xCoordB = normalizeB.getXCoord();
equals = xCoordA.equals(xCoordB);
if (!equals) {
return false;
}
ECFieldElement yCoordA = normalizeA.getYCoord();
ECFieldElement yCoordB = normalizeB.getYCoord();
equals = yCoordA.equals(yCoordB);
if (!equals) {
return false;
}
return true;
}
@SuppressWarnings("RedundantIfStatement")
public static
boolean compare(IESParameters cipherAParams, IESParameters cipherBParams) {
if (!Arrays.equals(cipherAParams.getDerivationV(), cipherBParams.getDerivationV())) {
return false;
}
if (!Arrays.equals(cipherAParams.getEncodingV(), cipherBParams.getEncodingV())) {
return false;
}
if (cipherAParams.getMacKeySize() != cipherBParams.getMacKeySize()) {
return false;
}
return true;
}
public static
boolean compare(IESWithCipherParameters cipherAParams, IESWithCipherParameters cipherBParams) {
if (cipherAParams.getCipherKeySize() != cipherBParams.getCipherKeySize()) {
return false;
}
// only need to cast one side.
return compare((IESParameters) cipherAParams, cipherBParams);
}
/**
* The message will have the (digestName) hash calculated and used for the signature.
* <p/>
* The returned signature is the {r,s} signature array.
*/
public static
BigInteger[] generateSignature(String digestName, ECPrivateKeyParameters privateKey, SecureRandom secureRandom, byte[] bytes) {
Digest digest = DigestFactory.getDigest(digestName);
byte[] checksum = new byte[digest.getDigestSize()];
digest.update(bytes, 0, bytes.length);
digest.doFinal(checksum, 0);
return generateSignatureForHash(privateKey, secureRandom, checksum);
}
/**
* The message will use the bytes AS THE HASHED VALUE to calculate the signature.
* <p/>
* The returned signature is the {r,s} signature array.
*/
public static
BigInteger[] generateSignatureForHash(ECPrivateKeyParameters privateKey, SecureRandom secureRandom, byte[] hashBytes) {
ParametersWithRandom param = new ParametersWithRandom(privateKey, secureRandom);
ECDSASigner ecdsa = new ECDSASigner();
ecdsa.init(true, param);
return ecdsa.generateSignature(hashBytes);
}
/**
* The message will have the (digestName) hash calculated and used for the signature.
*
* @param signature
* is the {r,s} signature array.
*
* @return true if the signature is valid
*/
public static
boolean verifySignature(String digestName, ECPublicKeyParameters publicKey, byte[] message, BigInteger[] signature) {
Digest digest = DigestFactory.getDigest(digestName);
byte[] checksum = new byte[digest.getDigestSize()];
digest.update(message, 0, message.length);
digest.doFinal(checksum, 0);
return verifySignatureHash(publicKey, checksum, signature);
}
/**
* The provided hash will be used in the signature verification.
*
* @param signature
* is the {r,s} signature array.
*
* @return true if the signature is valid
*/
public static
boolean verifySignatureHash(ECPublicKeyParameters publicKey, byte[] hash, BigInteger[] signature) {
ECDSASigner ecdsa = new ECDSASigner();
ecdsa.init(false, publicKey);
return ecdsa.verifySignature(hash, signature[0], signature[1]);
}
private
CryptoECC() {
}
}

View File

@ -1,834 +0,0 @@
/*
* Copyright 2015 dorkbox, llc
*
* 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.
*/
package dorkbox.util.crypto;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchProviderException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPCompressedDataGenerator;
import org.bouncycastle.openpgp.PGPEncryptedData;
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPLiteralDataGenerator;
import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPPublicKeyRing;
import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignature;
import org.bouncycastle.openpgp.PGPSignatureGenerator;
import org.bouncycastle.openpgp.PGPSignatureList;
import org.bouncycastle.openpgp.PGPSignatureSubpacketGenerator;
import org.bouncycastle.openpgp.PGPUtil;
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
import org.bouncycastle.openpgp.operator.bc.BcKeyFingerprintCalculator;
import org.bouncycastle.openpgp.operator.bc.BcPBESecretKeyDecryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPContentSignerBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDataEncryptorBuilder;
import org.bouncycastle.openpgp.operator.bc.BcPGPDigestCalculatorProvider;
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyKeyEncryptionMethodGenerator;
import dorkbox.util.IO;
/**
* PGP crypto related methods
*/
public final
class CryptoPGP {
private static final BcPGPDigestCalculatorProvider digestCalculatorProvider = new BcPGPDigestCalculatorProvider();
private static final BcKeyFingerprintCalculator fingerprintCalculator = new BcKeyFingerprintCalculator();
// https://github.com/weiliatgithub/bouncycastle-gpg-exampleC
// https://gist.github.com/turingbirds/3df43f1920a98010667a
// http://sloanseaman.com/wordpress/2012/05/13/revisited-pgp-encryptiondecryption-in-java/
// http://bouncycastle-pgp-cookbook.blogspot.de/
/**
* Sign a message using our private PGP key file, this matches gpg -ab "hello.txt"
*
* @param privateKeyInputStream
* this is an armored key file, not a binary stream
* @param userId
* this is the userID to get out of the private key
* @param password
* this is the password to unlock the private key
* @param messageAsUtf8Bytes
* this is the message, in bytes, to sign
*/
public static
byte[] signGpgCompatible(InputStream privateKeyInputStream, String userId, char[] password, byte[] messageAsUtf8Bytes)
throws PGPException {
// the signature type (in gpg terms), is "sigclass". gpg is BINARY_DOC (0x00)
return sign(privateKeyInputStream,
userId,
password,
new ByteArrayInputStream(messageAsUtf8Bytes),
PGPSignature.BINARY_DOCUMENT,
false,
true,
false,
false,
false);
}
/**
* Sign a message using our private PGP key file, this matches gpg -ab "hello.txt"
*
* @param privateKeyInputStream
* this is an armored key file, not a binary stream
* @param userId
* this is the userID to get out of the private key
* @param password
* this is the password to unlock the private key
* @param message
* this is the message to sign
*/
public static
byte[] signGpgCompatible(InputStream privateKeyInputStream, String userId, char[] password, InputStream message)
throws PGPException {
// the signature type (in gpg terms), is "sigclass". gpg is BINARY_DOC (0x00)
return sign(privateKeyInputStream,
userId,
password,
message,
PGPSignature.BINARY_DOCUMENT,
false,
true,
false,
false,
false);
}
/**
* Sign a message using our private PGP key file, this matches gpg -ab "hello.txt". This will save the signature of the passed-in
* file to file name + .asc
*
* @param privateKeyInputStream
* this is an armored key file, not a binary stream
* @param userId
* this is the userID to get out of the private key
* @param password
* this is the password to unlock the private key
* @param file
* this is the file to sign
*/
public static
void signGpgCompatible(InputStream privateKeyInputStream, String userId, char[] password, File file)
throws PGPException {
// the signature type (in gpg terms), is "sigclass". gpg is BINARY_DOC (0x00)
final byte[] sign = sign(privateKeyInputStream,
userId,
password,
file,
PGPSignature.BINARY_DOCUMENT,
false,
true,
false,
false,
false);
FileOutputStream fileOutputStream1 = null;
try {
fileOutputStream1 = new FileOutputStream(new File(file.getAbsolutePath() + ".asc"));
fileOutputStream1.write(sign);
fileOutputStream1.flush();
} catch (FileNotFoundException e) {
throw new PGPException("Unable to save signature to file " + file.getAbsolutePath() + ".asc", e);
} catch (IOException e) {
throw new PGPException("Unable to save signature to file " + file.getAbsolutePath() + ".asc", e);
} finally {
IO.close(fileOutputStream1);
}
}
/**
* Sign a message using our private PGP key file, with a variety of options
*/
@SuppressWarnings("Duplicates")
public static
byte[] sign(InputStream privateKeyInputStream,
String userId,
char[] password,
InputStream message,
int signatureType,
boolean compressSignature,
boolean asciiArmoredOutput,
boolean includeDataInSignature,
boolean generateUserIdSubPacket,
boolean generateOnePassVersion) throws PGPException {
List<PGPSecretKey> secretKeys = getSecretKeys(privateKeyInputStream, userId);
PGPSignatureGenerator signature = createSignature(secretKeys, password, signatureType, generateUserIdSubPacket);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
OutputStream outputStream = byteArrayOutputStream;
if (asciiArmoredOutput) {
outputStream = new ArmoredOutputStream(byteArrayOutputStream);
}
PGPCompressedDataGenerator compressedDataGenerator = null;
BCPGOutputStream bcOutputStream;
if (compressSignature) {
compressedDataGenerator = new PGPCompressedDataGenerator(PGPCompressedData.ZLIB);
try {
bcOutputStream = new BCPGOutputStream(compressedDataGenerator.open(outputStream));
} catch (IOException e) {
throw new PGPException("Unable to open compression stream in the signature", e);
}
}
else {
bcOutputStream = new BCPGOutputStream(outputStream);
}
if (generateOnePassVersion) {
try {
signature.generateOnePassVersion(false)
.encode(bcOutputStream);
} catch (IOException e) {
throw new PGPException("Unable to generate OnePass signature header", e);
}
}
PGPLiteralDataGenerator literalDataGenerator = null;
OutputStream literalDataOutput = null;
if (includeDataInSignature) {
literalDataGenerator = new PGPLiteralDataGenerator();
try {
literalDataOutput = literalDataGenerator.open(bcOutputStream,
PGPLiteralData.BINARY,
"_CONSOLE",
message.available(),
new Date());
} catch (IOException e1) {
throw new PGPException("Unable to generate Literal Data signature header", e1);
}
}
try {
byte[] buffer = new byte[4096];
int read;
// update bytes in the streams
if (literalDataOutput != null) {
while ((read = message.read(buffer)) > 0) {
literalDataOutput.write(buffer, 0, read);
signature.update(buffer, 0, read);
}
literalDataOutput.flush();
} else {
while ((read = message.read(buffer)) > 0) {
signature.update(buffer, 0, read);
}
}
// close generators and update signature
if (literalDataGenerator != null) {
literalDataGenerator.close();
}
signature.generate()
.encode(bcOutputStream);
if (compressedDataGenerator != null) {
compressedDataGenerator.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
IO.close(bcOutputStream);
IO.close(outputStream);
IO.close(literalDataOutput);
}
return byteArrayOutputStream.toByteArray();
}
/**
* Sign a message using our private PGP key file, with a variety of options
*/
@SuppressWarnings("Duplicates")
public static
byte[] sign(InputStream privateKeyInputStream,
String userId,
char[] password,
File fileMessage,
int signatureType,
boolean compressSignature,
boolean asciiArmoredOutput,
boolean includeDataInSignature,
boolean generateUserIdSubPacket,
boolean generateOnePassVersion) throws PGPException {
List<PGPSecretKey> secretKeys = getSecretKeys(privateKeyInputStream, userId);
PGPSignatureGenerator signature = createSignature(secretKeys, password, signatureType, generateUserIdSubPacket);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
OutputStream outputStream = byteArrayOutputStream;
if (asciiArmoredOutput) {
outputStream = new ArmoredOutputStream(byteArrayOutputStream);
}
PGPCompressedDataGenerator compressedDataGenerator = null;
BCPGOutputStream bcOutputStream;
if (compressSignature) {
compressedDataGenerator = new PGPCompressedDataGenerator(PGPCompressedData.ZLIB);
try {
bcOutputStream = new BCPGOutputStream(compressedDataGenerator.open(outputStream));
} catch (IOException e) {
throw new PGPException("Unable to open compression stream in the signature", e);
}
}
else {
bcOutputStream = new BCPGOutputStream(outputStream);
}
if (generateOnePassVersion) {
try {
signature.generateOnePassVersion(false)
.encode(bcOutputStream);
} catch (IOException e) {
throw new PGPException("Unable to generate OnePass signature header", e);
}
}
PGPLiteralDataGenerator literalDataGenerator = null;
OutputStream literalDataOutput = null;
if (includeDataInSignature) {
literalDataGenerator = new PGPLiteralDataGenerator();
try {
literalDataOutput = literalDataGenerator.open(bcOutputStream,
PGPLiteralData.BINARY,
fileMessage);
} catch (IOException e1) {
throw new PGPException("Unable to generate Literal Data signature header", e1);
}
}
try {
final FileInputStream fileInputStream = new FileInputStream(fileMessage);
byte[] buffer = new byte[4096];
int read;
// update bytes in the streams
if (literalDataOutput != null) {
while ((read = fileInputStream.read(buffer)) > 0) {
literalDataOutput.write(buffer, 0, read);
signature.update(buffer, 0, read);
}
literalDataOutput.flush();
} else {
while ((read = fileInputStream.read(buffer)) > 0) {
signature.update(buffer, 0, read);
}
}
// close generators and update signature
if (literalDataGenerator != null) {
literalDataGenerator.close();
}
signature.generate()
.encode(bcOutputStream);
if (compressedDataGenerator != null) {
compressedDataGenerator.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
IO.close(bcOutputStream);
IO.close(outputStream);
IO.close(literalDataOutput);
}
return byteArrayOutputStream.toByteArray();
}
/**
* Find private gpg key in InputStream, also closes the input stream
*
* @param inputStream
* the inputStream that contains the private (secret) key
* @param userId
* the user id
*
* @return the PGP secret key
*/
public static
List<PGPSecretKey> getSecretKeys(InputStream inputStream, String userId) throws PGPException {
// iterate over every private key in the key ring
PGPSecretKeyRingCollection secretKeyRings;
try {
secretKeyRings = new PGPSecretKeyRingCollection(PGPUtil.getDecoderStream(inputStream), fingerprintCalculator);
} catch (IOException e) {
throw new PGPException("No private key found in stream!", e);
} finally {
IO.close(inputStream);
}
// look for the key ring that is used to authenticate our reporting facilities
Iterator<PGPSecretKeyRing> secretKeys = secretKeyRings.getKeyRings(userId);
List<PGPSecretKey> pgpSecretKeys = new ArrayList<PGPSecretKey>();
// iterate over every private key in the ring
while (secretKeys.hasNext()) {
PGPSecretKeyRing secretKeyRing = secretKeys.next();
PGPSecretKey tmpKey = secretKeyRing.getSecretKey();
if (tmpKey != null) {
pgpSecretKeys.add(tmpKey);
}
}
if (!pgpSecretKeys.isEmpty()) {
return pgpSecretKeys;
}
throw new PGPException("No private key found in stream!");
}
/**
* Creates the signature that will be used to PGP sign data
*
* @param secretKeys
* these are the secret keys
* @param password
* this is the password to unlock the secret key
*
* @return the signature used to sign data
*
* @throws PGPException
*/
private static
PGPSignatureGenerator createSignature(List<PGPSecretKey> secretKeys,
char[] password,
int signatureType,
boolean generateUserIdSubPacket) throws PGPException {
PGPSecretKey secretKey = null;
for (int i = 0; i < secretKeys.size(); i++) {
secretKey = secretKeys.get(i);
// we ONLY want the signing master key
if (!secretKey.isSigningKey() || !secretKey.isMasterKey()) {
secretKey = null;
}
}
if (secretKey == null) {
throw new PGPException("Secret key is not the signing master key");
}
// System.err.println("Signing key = " + tmpKey.isSigningKey() +", Master key = " + tmpKey.isMasterKey() + ", UserId = " +
// userId );
if (password == null) {
password = new char[0];
}
PBESecretKeyDecryptor build = new BcPBESecretKeyDecryptorBuilder(digestCalculatorProvider).build(password);
SecureRandom random = new SecureRandom();
BcPGPContentSignerBuilder bcPGPContentSignerBuilder = new BcPGPContentSignerBuilder(secretKey.getPublicKey()
.getAlgorithm(),
PGPUtil.SHA1).setSecureRandom(random);
PGPSignatureGenerator signature = new PGPSignatureGenerator(bcPGPContentSignerBuilder);
signature.init(signatureType, secretKey.extractPrivateKey(build));
Iterator userIds = secretKey.getPublicKey()
.getUserIDs();
// use the first userId that matches
if (userIds.hasNext()) {
if (generateUserIdSubPacket) {
PGPSignatureSubpacketGenerator subpacketGenerator = new PGPSignatureSubpacketGenerator();
subpacketGenerator.addSignerUserID(false, (String) userIds.next());
signature.setHashedSubpackets(subpacketGenerator.generate());
}
else {
signature.setHashedSubpackets(null);
}
return signature;
}
else {
throw new PGPException("Did not find specified userId");
}
}
/**
* Decode a PGP public key block and return the keyring it represents.
*/
public static
PGPPublicKeyRing getKeyring(InputStream keyBlockStream) throws IOException {
BcKeyFingerprintCalculator keyfp = new BcKeyFingerprintCalculator();
// PGPUtil.getDecoderStream() will detect ASCII-armor automatically and decode it,
// the PGPObject factory then knows how to read all the data in the encoded stream
PGPObjectFactory factory = new PGPObjectFactory(PGPUtil.getDecoderStream(keyBlockStream), keyfp);
// these files should really just have one object in them, and that object should be a PGPPublicKeyRing.
Object o = factory.nextObject();
if (o instanceof PGPPublicKeyRing) {
return (PGPPublicKeyRing) o;
}
throw new IllegalArgumentException("Input stream does not contain a PGP Public Key");
}
/**
* Get the first encryption key from the given keyring.
*/
public static
PGPPublicKey getEncryptionKey(PGPPublicKeyRing keyRing) {
if (keyRing == null) {
return null;
}
// iterate over the keys on the ring, look for one which is suitable for encryption.
Iterator keys = keyRing.getPublicKeys();
PGPPublicKey key;
while (keys.hasNext()) {
key = (PGPPublicKey) keys.next();
if (key.isEncryptionKey()) {
return key;
}
}
return null;
}
/**
* Get the first decryption key from the given keyring.
*/
public
PGPSecretKey getDecryptionKey(PGPSecretKeyRing keyRing) {
if (keyRing == null) {
return null;
}
// iterate over the keys on the ring, look for one which is suitable for encryption.
Iterator keys = keyRing.getSecretKeys();
PGPSecretKey key;
while (keys.hasNext()) {
key = (PGPSecretKey) keys.next();
if (key.isMasterKey()) {
return key;
}
}
return null;
}
/**
* Encrypt plaintext message using public key from publickeyFile.
*
* @param message
* the message
*
* @return the string
*/
private
String encrypt(InputStream publicKeyInputStream, String message) throws PGPException, IOException, NoSuchProviderException {
// find the PGP key in the file
PGPPublicKey publicKey = findPublicGPGKey(publicKeyInputStream);
if (publicKey == null) {
System.err.println("Did not find public GPG key");
return null;
}
// Encode the string into bytes using utf-8
byte[] utf8Bytes = message.getBytes(StandardCharsets.UTF_8);
ByteArrayOutputStream compressedOutput = new ByteArrayOutputStream();
// compress bytes with zip
PGPLiteralDataGenerator literalDataGenerator = new PGPLiteralDataGenerator();
// the reason why we compress here is GPG not being able to decrypt our message input but if we do not compress.
// I guess pkzip compression also encodes only to GPG-friendly characters.
PGPCompressedDataGenerator compressedDataGenerator = new PGPCompressedDataGenerator(CompressionAlgorithmTags.ZIP);
try {
OutputStream literalDataOutput = literalDataGenerator.open(compressedOutput,
PGPLiteralData.BINARY,
"_CONSOLE",
utf8Bytes.length,
new Date());
// update bytes in the stream
literalDataOutput.write(utf8Bytes);
} catch (IOException e) {
// catch but close the streams in finally
throw e;
} finally {
compressedDataGenerator.close();
IO.close(compressedOutput);
}
SecureRandom random = new SecureRandom();
// now we have zip-compressed bytes
byte[] compressedBytes = compressedOutput.toByteArray();
BcPGPDataEncryptorBuilder bcPGPDataEncryptorBuilder = new BcPGPDataEncryptorBuilder(PGPEncryptedData.CAST5)
.setWithIntegrityPacket(true)
.setSecureRandom(random);
PGPEncryptedDataGenerator encryptedDataGenerator = new PGPEncryptedDataGenerator(bcPGPDataEncryptorBuilder);
// use public key to encrypt data
BcPublicKeyKeyEncryptionMethodGenerator encKeyGen = new BcPublicKeyKeyEncryptionMethodGenerator(publicKey)
.setSecureRandom(random);
encryptedDataGenerator.addMethod(encKeyGen);
// literalDataOutput --> compressedOutput --> ArmoredOutputStream --> ByteArrayOutputStream
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ArmoredOutputStream armoredOut = new ArmoredOutputStream(byteArrayOutputStream);
OutputStream encryptedOutput = null;
try {
encryptedOutput = encryptedDataGenerator.open(armoredOut, compressedBytes.length);
encryptedOutput.write(compressedBytes);
} catch (IOException e) {
throw e;
} catch (PGPException e) {
throw e;
} finally {
IO.close(encryptedOutput);
IO.close(armoredOut);
}
String encrypted = new String(byteArrayOutputStream.toByteArray());
System.err.println("Message: " + message);
System.err.println("Encrypted: " + encrypted);
return encrypted;
}
/**
* Find public gpg key in InputStream.
*
* @param inputStream
* the input stream
*
* @return the PGP public key
*/
private static
PGPPublicKey findPublicGPGKey(InputStream inputStream) throws IOException, PGPException {
// get all key rings in the input stream
PGPPublicKeyRingCollection publicKeyRingCollection = new PGPPublicKeyRingCollection(PGPUtil.getDecoderStream(inputStream), fingerprintCalculator);
System.err.println("key ring size: " + publicKeyRingCollection.size());
Iterator<PGPPublicKeyRing> keyRingIter = publicKeyRingCollection.getKeyRings();
// iterate over keyrings
while (keyRingIter.hasNext()) {
PGPPublicKeyRing keyRing = keyRingIter.next();
Iterator<PGPPublicKey> keyIter = keyRing.getPublicKeys();
// iterate over public keys in the key ring
while (keyIter.hasNext()) {
PGPPublicKey tmpKey = keyIter.next();
if (tmpKey == null) {
break;
}
Iterator<String> userIDs = tmpKey.getUserIDs();
ArrayList<String> strings = new ArrayList<String>();
while (userIDs.hasNext()) {
String next = userIDs.next();
strings.add(next);
}
System.err.println(
"Encryption key = " + tmpKey.isEncryptionKey() + ", Master key = " + tmpKey.isMasterKey() + ", UserId = " +
strings);
// we need a master encryption key
if (tmpKey.isEncryptionKey() && tmpKey.isMasterKey()) {
return tmpKey;
}
}
}
throw new PGPException("No public key found!");
}
private static
void verify(final InputStream publicKeyInputStream, final byte[] signature) throws Exception {
PGPPublicKey publicKey = findPublicGPGKey(publicKeyInputStream);
String text = new String(signature);
Pattern regex = Pattern.compile(
"-----BEGIN PGP SIGNED MESSAGE-----\\r?\\n.*?\\r?\\n\\r?\\n(.*)\\r?\\n(-----BEGIN PGP SIGNATURE-----\\r?\\n.*-----END PGP SIGNATURE-----)",
Pattern.CANON_EQ | Pattern.DOTALL);
Matcher regexMatcher = regex.matcher(text);
if (regexMatcher.find()) {
String dataText = regexMatcher.group(1);
String signText = regexMatcher.group(2);
ByteArrayInputStream dataIn = new ByteArrayInputStream(dataText.getBytes("UTF8"));
ByteArrayInputStream signIn = new ByteArrayInputStream(signText.getBytes("UTF8"));
InputStream signIn2 = PGPUtil.getDecoderStream(signIn);
PGPObjectFactory pgpFact = new PGPObjectFactory(signIn2, new BcKeyFingerprintCalculator());
PGPSignatureList p3 = null;
Object o;
try {
o = pgpFact.nextObject();
if (o == null) {
throw new Exception();
}
} catch (Exception ex) {
throw new Exception("Invalid input data");
}
if (o instanceof PGPCompressedData) {
PGPCompressedData c1 = (PGPCompressedData) o;
pgpFact = new PGPObjectFactory(c1.getDataStream(), new BcKeyFingerprintCalculator());
p3 = (PGPSignatureList) pgpFact.nextObject();
}
else {
p3 = (PGPSignatureList) o;
}
// PGPSignature sig = p3.get(0);
// PGPPublicKey key = KeyRing.getPublicKeyByID(sig.getKeyID());
//
// if (key == null)
// throw new Exception("Cannot find key 0x" + Integer.toHexString((int) sig.getKeyID()).toUpperCase() + " in the pubring");
//
// sig.initVerify(key, "BC");
//
// while ((ch = dataIn.read()) >= 0) {
// sig.update((byte) ch); //TODO migliorabile con byte[]
// }
//
// if (sig.verify())
// return new PrintablePGPPublicKey(key).toString();
// else
// return null;
// return verifyFile(dataIn, signIn);
}
}
private
CryptoPGP() {
}
public static
void main(String[] args) throws Exception {
InputStream privateKeyInputStream = new FileInputStream(new File("/home/user/dorkbox/sonatype_private.key"));
byte[] textBytes = "hello".getBytes(StandardCharsets.UTF_8);
byte[] bytes = CryptoPGP.signGpgCompatible(privateKeyInputStream, "Dorkbox <sonatype@dorkbox.com>", new char[0], textBytes);
// String s = new String(hello);
// String s1 = s.replaceAll("\n", "\r\n");
// byte[] bytes = s1.getBytes(OS.UTF_8);
//
// String signed = new String(bytes);
//
// System.err.println("Message: " + new String(messageAsUtf8Bytes));
// System.err.println("Signature: " + signed);
//
// return bytes;
// String s2 = new String(bytes);
// InputStream publicKeyInputStream = new FileInputStream(new File("/home/user/dorkbox/sonatype_public.key"));
// cryptoPGP.verify(publicKeyInputStream, hello);
FileOutputStream fileOutputStream = new FileOutputStream(new File("/home/user/dorkbox/hello2.txt"));
fileOutputStream.write(textBytes);
fileOutputStream.flush();
IO.close(fileOutputStream);
FileOutputStream fileOutputStream1 = new FileOutputStream(new File("/home/user/dorkbox/hello2.txt.asc"));
fileOutputStream1.write(bytes);
fileOutputStream1.flush();
IO.close(fileOutputStream1);
}
}

View File

@ -1,309 +0,0 @@
/*
* Copyright 2010 dorkbox, llc
*
* 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.
*/
package dorkbox.util.crypto;
import org.bouncycastle.crypto.AsymmetricBlockCipher;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.bouncycastle.crypto.signers.PSSSigner;
import org.slf4j.Logger;
import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.security.SecureRandom;
/**
* This is here just for keeping track of how this is done. This should NOT be used, and instead use ECC crypto.
*/
@Deprecated
public final
class CryptoRSA {
public static
AsymmetricCipherKeyPair generateKeyPair(SecureRandom secureRandom, int keyLength) {
RSAKeyPairGenerator keyGen = new RSAKeyPairGenerator();
RSAKeyGenerationParameters params = new RSAKeyGenerationParameters(new BigInteger("65537"), // public exponent
secureRandom, //pnrg
keyLength, // key length
8); //the number of iterations of the Miller-Rabin primality test.
keyGen.init(params);
return keyGen.generateKeyPair();
}
/**
* RSA encrypt using public key A, and sign data with private key B.
* <p/>
* byte[0][] = encrypted data byte[1][] = signature
*
* @param logger
* may be null, if no log output is necessary
*
* @return empty byte[][] if error
*/
public static
byte[][] encryptAndSign(AsymmetricBlockCipher rsaEngine,
Digest digest,
RSAKeyParameters rsaPublicKeyA,
RSAPrivateCrtKeyParameters rsaPrivateKeyB,
byte[] bytes,
Logger logger) {
if (bytes.length == 0) {
return new byte[0][0];
}
byte[] encryptBytes = encrypt(rsaEngine, rsaPublicKeyA, bytes, logger);
if (encryptBytes.length == 0) {
return new byte[0][0];
}
// now sign it.
PSSSigner signer = new PSSSigner(rsaEngine, digest, digest.getDigestSize());
byte[] signatureRSA = CryptoRSA.sign(signer, rsaPrivateKeyB, encryptBytes, logger);
if (signatureRSA.length == 0) {
return new byte[0][0];
}
byte[][] total = new byte[2][];
total[0] = encryptBytes;
total[1] = signatureRSA;
return total;
}
/**
* RSA verify data with public key B, and decrypt using private key A.
*
* @param logger
* may be null, if no log output is necessary
*
* @return empty byte[] if error
*/
public static
byte[] decryptAndVerify(AsymmetricBlockCipher rsaEngine,
Digest digest,
RSAKeyParameters rsaPublicKeyA,
RSAPrivateCrtKeyParameters rsaPrivateKeyB,
byte[] encryptedData,
byte[] signature,
Logger logger) {
if (encryptedData.length == 0 || signature.length == 0) {
return new byte[0];
}
// verify encrypted data.
PSSSigner signer = new PSSSigner(rsaEngine, digest, digest.getDigestSize());
boolean verify = verify(signer, rsaPublicKeyA, signature, encryptedData);
if (!verify) {
return new byte[0];
}
return decrypt(rsaEngine, rsaPrivateKeyB, encryptedData, logger);
}
/**
* RSA encrypts data with a specified key.
*
* @param logger
* may be null, if no log output is necessary
*
* @return empty byte[] if error
*/
public static
byte[] encrypt(AsymmetricBlockCipher rsaEngine, RSAKeyParameters rsaPublicKey, byte[] bytes, Logger logger) {
rsaEngine.init(true, rsaPublicKey);
try {
int inputBlockSize = rsaEngine.getInputBlockSize();
if (inputBlockSize < bytes.length) {
int outSize = rsaEngine.getOutputBlockSize();
//noinspection NumericCastThatLosesPrecision
int realsize = (int) Math.round(bytes.length / (outSize * 1.0D) + 0.5);
ByteBuffer buffer = ByteBuffer.allocateDirect(outSize * realsize);
int position = 0;
while (position < bytes.length) {
int size = Math.min(inputBlockSize, bytes.length - position);
byte[] block = rsaEngine.processBlock(bytes, position, size);
buffer.put(block, 0, block.length);
position += size;
}
return buffer.array();
}
else {
return rsaEngine.processBlock(bytes, 0, bytes.length);
}
} catch (Exception e) {
if (logger != null) {
logger.error("Unable to perform RSA cipher.", e);
}
return new byte[0];
}
}
/**
* RSA decrypt data with a specified key.
*
* @param logger
* may be null, if no log output is necessary
*
* @return empty byte[] if error
*/
public static
byte[] decrypt(AsymmetricBlockCipher rsaEngine, RSAPrivateCrtKeyParameters rsaPrivateKey, byte[] bytes, Logger logger) {
rsaEngine.init(false, rsaPrivateKey);
try {
int inputBlockSize = rsaEngine.getInputBlockSize();
if (inputBlockSize < bytes.length) {
int outSize = rsaEngine.getOutputBlockSize();
//noinspection NumericCastThatLosesPrecision
int realsize = (int) Math.round(bytes.length / (outSize * 1.0D) + 0.5);
ByteArrayOutputStream buffer = new ByteArrayOutputStream(outSize * realsize);
int position = 0;
while (position < bytes.length) {
int size = Math.min(inputBlockSize, bytes.length - position);
byte[] block = rsaEngine.processBlock(bytes, position, size);
buffer.write(block, 0, block.length);
position += size;
}
return buffer.toByteArray();
}
else {
return rsaEngine.processBlock(bytes, 0, bytes.length);
}
} catch (Exception e) {
if (logger != null) {
logger.error("Unable to perform RSA cipher.", e);
}
return new byte[0];
}
}
/**
* RSA sign data with a specified key.
*
* @param logger
* may be null, if no log output is necessary
*
* @return empty byte[] if error
*/
public static
byte[] sign(PSSSigner signer, RSAPrivateCrtKeyParameters rsaPrivateKey, byte[] mesg, Logger logger) {
signer.init(true, rsaPrivateKey);
signer.update(mesg, 0, mesg.length);
try {
return signer.generateSignature();
} catch (Exception e) {
if (logger != null) {
logger.error("Unable to perform RSA cipher.", e);
}
return new byte[0];
}
}
/**
* RSA verify data with a specified key.
*/
public static
boolean verify(PSSSigner signer, RSAKeyParameters rsaPublicKey, byte[] sig, byte[] mesg) {
signer.init(false, rsaPublicKey);
signer.update(mesg, 0, mesg.length);
return signer.verifySignature(sig);
}
@SuppressWarnings("RedundantIfStatement")
public static
boolean compare(RSAKeyParameters publicA, RSAKeyParameters publicB) {
if (!publicA.getExponent()
.equals(publicB.getExponent())) {
return false;
}
if (!publicA.getModulus()
.equals(publicB.getModulus())) {
return false;
}
return true;
}
@SuppressWarnings("RedundantIfStatement")
public static
boolean compare(RSAPrivateCrtKeyParameters private1, RSAPrivateCrtKeyParameters private2) {
if (!private1.getModulus()
.equals(private2.getModulus())) {
return false;
}
if (!private1.getExponent()
.equals(private2.getExponent())) {
return false;
}
if (!private1.getDP()
.equals(private2.getDP())) {
return false;
}
if (!private1.getDQ()
.equals(private2.getDQ())) {
return false;
}
if (!private1.getP()
.equals(private2.getP())) {
return false;
}
if (!private1.getPublicExponent()
.equals(private2.getPublicExponent())) {
return false;
}
if (!private1.getQ()
.equals(private2.getQ())) {
return false;
}
if (!private1.getQInv()
.equals(private2.getQInv())) {
return false;
}
return true;
}
private
CryptoRSA() {
}
}

View File

@ -1,236 +0,0 @@
/*
* Copyright 2010 dorkbox, llc
*
* 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.
*/
package dorkbox.util.crypto;
import java.security.SecureRandom;
import java.util.Arrays;
import java.util.Base64;
/**
* An implementation of the <a href="http://www.tarsnap.com/scrypt/scrypt.pdf"/>scrypt</a> key derivation function.
*/
public final
class CryptoSCrypt {
/**
* Hash the supplied plaintext password and generate output using default parameters
* <p/>
* The password chars are no longer valid after this call
*
* @param password
* Password.
*/
public static
String encrypt(char[] password) {
return encrypt(password, 16384, 32, 1);
}
/**
* Hash the supplied plaintext password and generate output using default parameters
* <p/>
* The password chars are no longer valid after this call
*
* @param password
* Password.
* @param salt
* Salt parameter
*/
public static
String encrypt(char[] password, byte[] salt) {
return encrypt(password, salt, 16384, 128, 1, 64);
}
/**
* Hash the supplied plaintext password and generate output.
* <p/>
* The password chars are no longer valid after this call
*
* @param password
* Password.
* @param N
* CPU cost parameter.
* @param r
* Memory cost parameter.
* @param p
* Parallelization parameter.
*
* @return The hashed password.
*/
public static
String encrypt(char[] password, int N, int r, int p) {
SecureRandom secureRandom = new SecureRandom();
byte[] salt = new byte[32];
secureRandom.nextBytes(salt);
return encrypt(password, salt, N, r, p, 64);
}
/**
* Hash the supplied plaintext password and generate output.
* <p/>
* The password chars are no longer valid after this call
*
* @param password
* Password.
* @param salt
* Salt parameter
* @param N
* CPU cost parameter.
* @param r
* Memory cost parameter.
* @param p
* Parallelization parameter.
* @param dkLen
* Intended length of the derived key.
*
* @return The hashed password.
*/
public static
String encrypt(char[] password, byte[] salt, int N, int r, int p, int dkLen) {
// Note: this saves the char array in UTF-16 format of bytes.
// can't use password after this as it's been changed to '*'
byte[] passwordBytes = Crypto.charToBytesPassword_UTF16(password);
byte[] derived = encrypt(passwordBytes, salt, N, r, p, dkLen);
String params = Integer.toString(log2(N) << 16 | r << 8 | p, 16);
@SuppressWarnings("StringBufferReplaceableByString")
StringBuilder sb = new StringBuilder((salt.length + derived.length) * 2);
sb.append("$s0$")
.append(params)
.append('$');
sb.append(Base64.getEncoder().encodeToString(salt))
.append('$');
sb.append(Base64.getEncoder().encodeToString(derived));
return sb.toString();
}
/**
* Compare the supplied plaintext password to a hashed password.
*
* @param password
* Plaintext password.
* @param hashed
* scrypt hashed password.
*
* @return true if password matches hashed value.
*/
public static
boolean verify(char[] password, String hashed) {
// Note: this saves the char array in UTF-16 format of bytes.
// can't use password after this as it's been changed to '*'
byte[] passwordBytes = Crypto.charToBytesPassword_UTF16(password);
String[] parts = hashed.split("\\$");
if (parts.length != 5 || !parts[1].equals("s0")) {
throw new IllegalArgumentException("Invalid hashed value");
}
int params = Integer.parseInt(parts[2], 16);
byte[] salt = Base64.getDecoder().decode(parts[3]);
byte[] derived0 = Base64.getDecoder().decode(parts[4]);
//noinspection NumericCastThatLosesPrecision
int N = (int) Math.pow(2, params >> 16 & 0xFF);
int r = params >> 8 & 0xFF;
int p = params & 0xFF;
int length = derived0.length;
if (length == 0) {
return false;
}
byte[] derived1 = encrypt(passwordBytes, salt, N, r, p, length);
if (length != derived1.length) {
return false;
}
int result = 0;
for (int i = 0; i < length; i++) {
result |= derived0[i] ^ derived1[i];
}
return result == 0;
}
private static
int log2(int n) {
int log = 0;
if ((n & 0xFFFF0000) != 0) {
n >>>= 16;
log = 16;
}
if (n >= 256) {
n >>>= 8;
log += 8;
}
if (n >= 16) {
n >>>= 4;
log += 4;
}
if (n >= 4) {
n >>>= 2;
log += 2;
}
return log + (n >>> 1);
}
/**
* Pure Java implementation of the <a href="http://www.tarsnap.com/scrypt/scrypt.pdf"/>scrypt KDF</a>.
*
* @param password
* Password.
* @param salt
* Salt.
* @param N
* CPU cost parameter.
* @param r
* Memory cost parameter.
* @param p
* Parallelization parameter.
* @param dkLen
* Intended length of the derived key.
*
* @return The derived key.
*/
public static
byte[] encrypt(byte[] password, byte[] salt, int N, int r, int p, int dkLen) {
if (N == 0 || (N & N - 1) != 0) {
throw new IllegalArgumentException("N must be > 0 and a power of 2");
}
if (N > Integer.MAX_VALUE / 128 / r) {
throw new IllegalArgumentException("Parameter N is too large");
}
if (r > Integer.MAX_VALUE / 128 / p) {
throw new IllegalArgumentException("Parameter r is too large");
}
try {
return org.bouncycastle.crypto.generators.SCrypt.generate(password, salt, N, r, p, dkLen);
} finally {
// now zero out the bytes in password.
Arrays.fill(password, (byte) 0);
}
}
private
CryptoSCrypt() {
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,336 +0,0 @@
package dorkbox.util.crypto
import java.io.File
import java.io.IOException
import java.nio.file.Files
import java.security.GeneralSecurityException
import java.security.MessageDigest
import java.security.SecureRandom
import java.util.*
import javax.crypto.BadPaddingException
import javax.crypto.Cipher
import javax.crypto.IllegalBlockSizeException
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
object OpenSSLDecryptor {
private val INDEX_KEY = 0
private val INDEX_IV = 1
private val ITERATIONS = 1
private val ARG_INDEX_FILENAME = 0
private val ARG_INDEX_PASSWORD = 1
private val SALT_OFFSET = 8
private val SALT_SIZE = 8
private val CIPHERTEXT_OFFSET = SALT_OFFSET + SALT_SIZE
private val KEY_SIZE_BITS = 256
/**
* Thanks go to Ola Bini for releasing this source on his blog.
* The source was obtained from [here](http://olabini.com/blog/tag/evp_bytestokey/) .
*/
fun EVP_BytesToKey(key_len: Int, iv_len: Int, md: MessageDigest, salt: ByteArray?, data: ByteArray?, count: Int): Array<ByteArray> {
val key = ByteArray(key_len)
var key_ix = 0
val iv = ByteArray(iv_len)
var iv_ix = 0
val both = arrayOf(key, iv)
if (data == null) {
return both
}
var md_buf: ByteArray? = null
var nkey = key_len
var niv = iv_len
var i: Int
var addmd = 0
while (true) {
md.reset()
if (addmd++ > 0) {
md.update(md_buf!!)
}
md.update(data)
if (null != salt) {
md.update(salt, 0, 8)
}
md_buf = md.digest()
i = 1
while (i < count) {
md.reset()
md.update(md_buf!!)
md_buf = md.digest()
i++
}
i = 0
if (nkey > 0) {
while (true) {
if (nkey == 0) {
break
}
if (i == md_buf!!.size) {
break
}
key[key_ix++] = md_buf[i]
nkey--
i++
}
}
if (niv > 0 && i != md_buf!!.size) {
while (true) {
if (niv == 0) {
break
}
if (i == md_buf.size) {
break
}
iv[iv_ix++] = md_buf[i]
niv--
i++
}
}
if (nkey == 0 && niv == 0) {
break
}
}
i = 0
while (i < md_buf!!.size) {
md_buf[i] = 0
i++
}
return both
}
@JvmStatic
fun main(args: Array<String>) {
// /usr/bin/openssl enc -d -aes-256-cbc -md sha256 -in update_file.encrypted -out update_file.bin -pass pass:xyfjWNl6yPIZfRYLu64L2sleiF8vD5xgHsJ3sa3Ya6sY01
// NON-PBKDF2, WITH BASE64
// openssl enc -aes-256-cbc -a -salt -md sha256 -in password.txt -out password.txt.enc -pass pass:xyfjWNl6yPIZfRYLu64L2sleiF8vD5xgHsJ3sa3Ya6sY01
// openssl enc -aes-256-cbc -a -d -md sha256 -in password.txt.enc -out password.txt.new -pass pass:xyfjWNl6yPIZfRYLu64L2sleiF8vD5xgHsJ3sa3Ya6sY01 && cat password.txt.new
// NON-PBKDF2, WITHOUT BASE64
// openssl enc -aes-256-cbc -salt -md sha256 -in password.txt -out password.txt.enc -pass pass:xyfjWNl6yPIZfRYLu64L2sleiF8vD5xgHsJ3sa3Ya6sY01
// openssl enc -aes-256-cbc -d -md sha256 -in password.txt.enc -out password.txt.new -pass pass:xyfjWNl6yPIZfRYLu64L2sleiF8vD5xgHsJ3sa3Ya6sY01 && cat password.txt.new
// PBKDF2 (NOT WORKING)
// openssl aes-256-cbc -a -salt -pbkdf2 -iter 1 -md sha256 -in password.txt -out password.txt.enc -pass pass:xyfjWNl6yPIZfRYLu64L2sleiF8vD5xgHsJ3sa3Ya6sY01
// openssl aes-256-cbc -d -a -md sha256 -pbkdf2 -iter 1 -in password.txt.enc -out password.txt.new -pass pass:xyfjWNl6yPIZfRYLu64L2sleiF8vD5xgHsJ3sa3Ya6sY01 && cat password.txt.new
val decrypt = false
try {
val password = "xyfjWNl6yPIZfRYLu64L2sleiF8vD5xgHsJ3sa3Ya6sY01"
val passwordBytes = password.toByteArray(Charsets.US_ASCII)
// openssl enc -aes-256-cbc -d -md sha256 -in install_2019.1.bin.enc -out install_2019.1.bin.new -pass pass:xyfjWNl6yPIZfRYLu64L2sleiF8vD5xgHsJ3sa3Ya6sY01
// val plaintextFileName = "build/install_2019.1.bin"
// val encryptedFileName = "build/install_2019.1.bin.enc"
// val fileOutputStream = FileOutputStream(File(encryptedFileName))
// OpenSSLPBEOutputStream(fileOutputStream, password).use { outputStream ->
// Files.copy(Path.of(plaintextFileName), outputStream)
// }
//
// if (true) {
// return
// }
val plaintextFileName = "password.txt"
val encryptedFileName = "password.txt.enc"
if (decrypt) {
// --- read base 64 encoded file ---
val encryptedFile = File(encryptedFileName).absoluteFile
// this is WITH BASE64 (with openssl -a)
// val lines = Files.readAllLines(encryptedFile.toPath(), Charsets.US_ASCII)
// val sb = StringBuilder()
// for (line in lines) {
// sb.append(line.trim())
// }
// val dataBase64 = sb.toString()
// val headerSaltAndCipherText = Base64.getDecoder().decode(dataBase64) // when base64 encoded
// this is NOT base64
val headerSaltAndCipherText = encryptedFile.readBytes()
// --- extract salt & encrypted ---
// header is "Salted__", ASCII encoded, if salt is being used (the default)
val salt = Arrays.copyOfRange(headerSaltAndCipherText, SALT_OFFSET, SALT_OFFSET + SALT_SIZE)
val encrypted = Arrays.copyOfRange(headerSaltAndCipherText, CIPHERTEXT_OFFSET, headerSaltAndCipherText.size)
// --- specify cipher and digest for EVP_BytesToKey method ---
val aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding")
val md = MessageDigest.getInstance("SHA-256")
// --- create key and IV ---
// the IV is useless, OpenSSL might as well have use zero's
// val keyAndIV = EVP_BytesToKey(KEY_SIZE_BITS / java.lang.Byte.SIZE, aesCBC.blockSize, md, salt, passString.toByteArray(ASCII), ITERATIONS)
// val key = SecretKeySpec(keyAndIV[INDEX_KEY], "AES")
// val iv = IvParameterSpec(keyAndIV[INDEX_IV])
/////////////////////
// val openssl = OpenSSLPBEParametersGenerator()
// openssl.init(passString.toByteArray(Charsets.US_ASCII), salt)
// val keyAndIV = openssl.generateDerivedParameters(256)
//
// val keyBytes = Arrays.copyOfRange(keyAndIV, 0, 32)
// val ivBytes = Arrays.copyOfRange(keyAndIV, 32, 48)
//
// val key = SecretKeySpec(keyBytes, "AES")
// val iv = IvParameterSpec(ivBytes)
//////////////////////
md.update(passwordBytes)
md.update(salt)
var hash = md.digest()
var keyAndIV = hash.clone()
// 1 round
md.update(hash)
md.update(passwordBytes)
md.update(salt)
hash = md.digest()
keyAndIV = concat(keyAndIV, hash)
val keyBytes = Arrays.copyOfRange(keyAndIV, 0, 32)
val ivBytes = Arrays.copyOfRange(keyAndIV, 32, 48)
val key = SecretKeySpec(keyBytes, "AES")
val iv = IvParameterSpec(ivBytes)
///////////////////
// val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
// val spec = PBEKeySpec(passString.toCharArray(), salt, ITERATIONS, KEY_SIZE_BITS)
// val tmp = factory.generateSecret(spec)
// val key = SecretKeySpec(tmp.encoded, "AES")
//
//
// val ivBytes = byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
// val iv = IvParameterSpec(ivBytes)
/////////////////////////
// --- initialize cipher instance and decrypt ---
aesCBC.init(Cipher.DECRYPT_MODE, key, iv)
val decrypted = aesCBC.doFinal(encrypted)
val answer = String(decrypted, Charsets.UTF_8)
println(answer)
}
else {
// read plaintext file
val plainTextFile = File(plaintextFileName).absoluteFile
val data= plainTextFile.readBytes()
// --- create salt ---
val salt = ByteArray(8)
val secureRandom = SecureRandom()
secureRandom.nextBytes(salt)
// --- specify cipher and digest for EVP_BytesToKey method ---
val aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding")
val md = MessageDigest.getInstance("SHA-256")
//////////////////////////////////////////
// the IV is useless, OpenSSL might as well have use zero's
// val keyAndIV = EVP_BytesToKey(KEY_SIZE_BITS / java.lang.Byte.SIZE, aesCBC.blockSize, md, salt,
// passString.toByteArray(Charsets.US_ASCII), ITERATIONS)
// val key = SecretKeySpec(keyAndIV[INDEX_KEY], "AES")
// val iv = IvParameterSpec(keyAndIV[INDEX_IV])
//////////////////////////////////////////
md.update(passwordBytes)
md.update(salt)
var hash = md.digest()
var keyAndIV = hash.clone()
// 1 round
md.update(hash)
md.update(passwordBytes)
md.update(salt)
hash = md.digest()
keyAndIV = concat(keyAndIV, hash)
val keyBytes = Arrays.copyOfRange(keyAndIV, 0, 32)
val ivBytes = Arrays.copyOfRange(keyAndIV, 32, 48)
val key = SecretKeySpec(keyBytes, "AES")
val iv = IvParameterSpec(ivBytes)
//////////////////////////////////////////
// val factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256")
// val spec = PBEKeySpec(passString.toCharArray(), salt, ITERATIONS, KEY_SIZE_BITS)
// val tmp = factory.generateSecret(spec)
// val key = SecretKeySpec(tmp.encoded, "AES")
//
//
// val ivBytes = byteArrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
// val iv = IvParameterSpec(ivBytes)
/////////////////////////
// --- initialize cipher instance and encrypt ---
aesCBC.init(Cipher.ENCRYPT_MODE, key, iv)
val encrypted = aesCBC.doFinal(data)
// val cipher = OpenSSLPBECommon.initializeCipher(password, salt, Cipher.ENCRYPT_MODE)
// val encrypted = cipher.doFinal(data)
// "Salted__" + salt + encrypted
val a1 = "Salted__".toByteArray(Charsets.US_ASCII)
val finalEncrypted = concat(concat(a1, salt), encrypted)
val encryptedFile = File(encryptedFileName).absoluteFile
Files.deleteIfExists(encryptedFile.toPath())
// WITH BASE64 By default the encoded file has a line break every 64 characters
// encryptedFile.writeBytes(Base64.getMimeEncoder(64, "\n".toByteArray(Charsets.US_ASCII)).encode(finalEncrypted))
// No base64
encryptedFile.writeBytes(finalEncrypted)
}
}
catch (e: BadPaddingException) {
// AKA "something went wrong"
throw IllegalStateException("Bad password, algorithm, mode or padding;" + " no salt, wrong number of iterations or corrupted ciphertext.")
}
catch (e: IllegalBlockSizeException) {
throw IllegalStateException("Bad algorithm, mode or corrupted (resized) ciphertext.")
}
catch (e: GeneralSecurityException) {
throw IllegalStateException(e)
}
catch (e: IOException) {
throw IllegalStateException(e)
}
}
fun concat(a: ByteArray, b: ByteArray): ByteArray {
val c = ByteArray(a.size + b.size)
System.arraycopy(a, 0, c, 0, a.size)
System.arraycopy(b, 0, c, a.size, b.size)
return c
}
}

View File

@ -1,75 +0,0 @@
package dorkbox.util.crypto
import java.security.InvalidAlgorithmParameterException
import java.security.InvalidKeyException
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
import java.security.spec.InvalidKeySpecException
import javax.crypto.Cipher
import javax.crypto.NoSuchPaddingException
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
// https://stackoverflow.com/questions/11783062/how-to-decrypt-file-in-java-encrypted-with-openssl-command-using-aes/11786924#11786924
internal object OpenSSLPBECommon {
const val SALT_SIZE_BYTES = 8
const val OPENSSL_HEADER_STRING = "Salted__"
val OPENSSL_HEADER_STRING_BYTES = OPENSSL_HEADER_STRING.toByteArray(Charsets.US_ASCII)
private val hashDigest = MessageDigest.getInstance("SHA-256")
fun toByteArray(chars: CharArray): ByteArray {
val bytes = ByteArray(chars.size)
for (i in bytes.indices) {
bytes[i] = chars[i].code.toByte()
}
return bytes
}
@Throws(NoSuchAlgorithmException::class,
InvalidKeySpecException::class,
InvalidKeyException::class,
NoSuchPaddingException::class,
InvalidAlgorithmParameterException::class
)
fun initializeCipher(password: String, salt: ByteArray, cipherMode: Int): Cipher {
val passwordBytes = password.toByteArray(Charsets.US_ASCII)
hashDigest.update(passwordBytes)
hashDigest.update(salt)
var hash = hashDigest.digest()
var keyAndIV = hash.clone()
// 1 round
hashDigest.update(hash)
hashDigest.update(passwordBytes)
hashDigest.update(salt)
hash = hashDigest.digest()
keyAndIV = concat(keyAndIV, hash)
val keyBytes = keyAndIV.copyOfRange(0, 32)
val ivBytes = keyAndIV.copyOfRange(32, 48)
val key = SecretKeySpec(keyBytes, "AES")
val iv = IvParameterSpec(ivBytes)
val cipher = Cipher.getInstance("AES/CBC/PKCS5PADDING")
cipher.init(cipherMode, key, iv);
return cipher
}
private
fun concat(a: ByteArray, b: ByteArray): ByteArray {
val c = ByteArray(a.size + b.size)
System.arraycopy(a, 0, c, 0, a.size)
System.arraycopy(b, 0, c, a.size, b.size)
return c
}
}

View File

@ -1,104 +0,0 @@
package dorkbox.util.crypto
import java.io.IOException
import java.io.InputStream
import java.security.InvalidAlgorithmParameterException
import java.security.InvalidKeyException
import java.security.NoSuchAlgorithmException
import java.security.spec.InvalidKeySpecException
import javax.crypto.BadPaddingException
import javax.crypto.Cipher
import javax.crypto.IllegalBlockSizeException
import javax.crypto.NoSuchPaddingException
import kotlin.experimental.and
import kotlin.jvm.Throws
// https://stackoverflow.com/questions/11783062/how-to-decrypt-file-in-java-encrypted-with-openssl-command-using-aes/11786924#11786924
class OpenSSLPBEInputStream @Throws(IOException::class)
constructor(private val inStream: InputStream, password: String) : InputStream() {
companion object {
private const val READ_BLOCK_SIZE = 64 * 1024
}
private val cipher: Cipher
private val bufferCipher = ByteArray(READ_BLOCK_SIZE)
private var bufferClear: ByteArray? = null
private var index = Integer.MAX_VALUE
private var maxIndex = 0
init {
try {
val salt = readSalt()
cipher = OpenSSLPBECommon.initializeCipher(password, salt, Cipher.DECRYPT_MODE)
}
catch (e: InvalidKeySpecException) {
throw IOException(e)
}
catch (e: NoSuchPaddingException) {
throw IOException(e)
}
catch (e: NoSuchAlgorithmException) {
throw IOException(e)
}
catch (e: InvalidKeyException) {
throw IOException(e)
}
catch (e: InvalidAlgorithmParameterException) {
throw IOException(e)
}
}
@Throws(IOException::class)
private fun readSalt(): ByteArray {
val headerBytes = ByteArray(OpenSSLPBECommon.OPENSSL_HEADER_STRING.length)
inStream.read(headerBytes)
val headerString = String(headerBytes, Charsets.US_ASCII)
if (OpenSSLPBECommon.OPENSSL_HEADER_STRING != headerString) {
throw IOException("unexpected magic bytes $headerString")
}
val salt = ByteArray(OpenSSLPBECommon.SALT_SIZE_BYTES)
inStream.read(salt)
return salt
}
@Throws(IOException::class)
override fun read(): Int {
if (index > maxIndex) {
index = 0
val read = inStream.read(bufferCipher)
if (read != -1) {
bufferClear = cipher.update(bufferCipher, 0, read)
}
if (read == -1 || bufferClear == null || bufferClear!!.isEmpty()) {
try {
bufferClear = cipher.doFinal()
}
catch (e: IllegalBlockSizeException) {
bufferClear = null
}
catch (e: BadPaddingException) {
bufferClear = null
}
}
if (bufferClear == null || bufferClear!!.isEmpty()) {
return -1
}
maxIndex = bufferClear!!.size - 1
}
return (bufferClear!![index++] and 0xFF.toByte()).toInt()
}
@Throws(IOException::class)
override fun available(): Int {
return inStream.available()
}
}

View File

@ -1,89 +0,0 @@
package dorkbox.util.crypto
import java.io.IOException
import java.io.OutputStream
import java.security.InvalidAlgorithmParameterException
import java.security.NoSuchAlgorithmException
import java.security.SecureRandom
import java.security.spec.InvalidKeySpecException
import javax.crypto.BadPaddingException
import javax.crypto.Cipher
import javax.crypto.IllegalBlockSizeException
import javax.crypto.NoSuchPaddingException
import kotlin.jvm.Throws
// https://stackoverflow.com/questions/11783062/how-to-decrypt-file-in-java-encrypted-with-openssl-command-using-aes/11786924#11786924
class OpenSSLPBEOutputStream @Throws(IOException::class)
constructor(private val outStream: OutputStream, password: String) : OutputStream() {
companion object {
private const val BUFFER_SIZE = 5 * 1024 * 1024
}
private val cipher: Cipher
private val buffer = ByteArray(BUFFER_SIZE)
private var bufferIndex: Int = 0
init {
try {
// Create and use a random SALT for each instance of this output stream.
val salt = ByteArray(OpenSSLPBECommon.SALT_SIZE_BYTES)
val secureRandom = SecureRandom()
secureRandom.nextBytes(salt)
cipher = OpenSSLPBECommon.initializeCipher(password, salt, Cipher.ENCRYPT_MODE)
// Write header
outStream.write(OpenSSLPBECommon.OPENSSL_HEADER_STRING_BYTES)
outStream.write(salt)
}
catch (e: InvalidKeySpecException) {
throw IOException(e)
}
catch (e: NoSuchPaddingException) {
throw IOException(e)
}
catch (e: NoSuchAlgorithmException) {
throw IOException(e)
}
catch (e: InvalidAlgorithmParameterException) {
throw IOException(e)
}
}
@Throws(IOException::class)
override fun write(b: Int) {
buffer[bufferIndex] = b.toByte()
bufferIndex++
// only update the digest and write out the buffer if it's enough (this is a slow operation)
if (bufferIndex == BUFFER_SIZE) {
val result = cipher.update(buffer, 0, bufferIndex)
outStream.write(result)
bufferIndex = 0
}
}
@Throws(IOException::class)
override fun flush() {
if (bufferIndex > 0) {
val result: ByteArray
try {
result = cipher.doFinal(buffer, 0, bufferIndex)
outStream.write(result)
}
catch (e: IllegalBlockSizeException) {
throw IOException(e)
}
catch (e: BadPaddingException) {
throw IOException(e)
}
bufferIndex = 0
}
}
@Throws(IOException::class)
override fun close() {
flush()
outStream.close()
}
}

View File

@ -1,17 +0,0 @@
/*
* Copyright 2021 dorkbox, llc
*
* 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.
*/
package dorkbox.util.crypto;

View File

@ -1,43 +0,0 @@
/*
* Copyright 2013 dorkbox, llc
*
* 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.
*/
package dorkbox.util.crypto.signers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.Signer;
import org.bouncycastle.crypto.signers.DSADigestSigner;
import org.bouncycastle.crypto.signers.ECDSASigner;
import org.bouncycastle.jcajce.provider.util.DigestFactory;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.bc.BcContentSignerBuilder;
public
class BcECDSAContentSignerBuilder extends BcContentSignerBuilder {
public
BcECDSAContentSignerBuilder(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier digAlgId) {
super(sigAlgId, digAlgId);
}
@Override
protected
Signer createSigner(AlgorithmIdentifier sigAlgId, AlgorithmIdentifier digAlgId) throws OperatorCreationException {
Digest digest = DigestFactory.getDigest(digAlgId.getAlgorithm()
.getId()); // SHA1, SHA512, etc
return new DSADigestSigner(new ECDSASigner(), digest);
}
}

View File

@ -1,56 +0,0 @@
/*
* Copyright 2013 dorkbox, llc
*
* 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.
*/
package dorkbox.util.crypto.signers;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.Signer;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.signers.DSADigestSigner;
import org.bouncycastle.crypto.signers.ECDSASigner;
import org.bouncycastle.crypto.util.PublicKeyFactory;
import org.bouncycastle.jcajce.provider.util.DigestFactory;
import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.bc.BcContentVerifierProviderBuilder;
import java.io.IOException;
public
class BcECDSAContentVerifierProviderBuilder extends BcContentVerifierProviderBuilder {
public
BcECDSAContentVerifierProviderBuilder(DigestAlgorithmIdentifierFinder digestAlgorithmFinder) {
}
@Override
protected
AsymmetricKeyParameter extractKeyParameters(SubjectPublicKeyInfo publicKeyInfo) throws IOException {
return PublicKeyFactory.createKey(publicKeyInfo);
}
@Override
protected
Signer createSigner(AlgorithmIdentifier sigAlgId) throws OperatorCreationException {
AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
Digest digest = DigestFactory.getDigest(digAlgId.getAlgorithm()
.getId()); // 1.2.23.4.5.6, etc
return new DSADigestSigner(new ECDSASigner(), digest);
}
}

View File

@ -1,17 +0,0 @@
/*
* Copyright 2021 dorkbox, llc
*
* 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.
*/
package dorkbox.util.crypto.signers;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2010 dorkbox, llc
* Copyright 2023 dorkbox, llc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -15,10 +15,16 @@
*/
package dorkbox.util.entropy
import dorkbox.util.Sys
import dorkbox.util.exceptions.InitializationException
import org.slf4j.LoggerFactory
object Entropy {
/**
* Gets the version number.
*/
val version = Sys.version
private var provider: EntropyProvider? = null
/**

View File

@ -1,165 +0,0 @@
/*
* Copyright 2010 dorkbox, llc
*
* 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.
*/
package dorkbox.util.properties;
import java.awt.Color;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
import dorkbox.util.FileUtil;
public
class PropertiesProvider {
private final Properties properties = new SortedProperties();
private final File propertiesFile;
private String comments = "Settings and configuration file. Strings must be escape formatted!";
public
PropertiesProvider(String propertiesFile) {
this(new File(propertiesFile));
}
public
PropertiesProvider(File propertiesFile) {
if (propertiesFile == null) {
throw new NullPointerException("propertiesFile");
}
propertiesFile = FileUtil.INSTANCE.normalize(propertiesFile);
// make sure the parent dir exists...
File parentFile = propertiesFile.getParentFile();
if (parentFile != null && !parentFile.exists()) {
if (!parentFile.mkdirs()) {
throw new RuntimeException("Unable to create directories for: " + propertiesFile);
}
}
this.propertiesFile = propertiesFile;
_load();
}
public
void setComments(String comments) {
this.comments = comments;
}
private
void _load() {
if (!this.propertiesFile.canRead() || !this.propertiesFile.exists()) {
// in this case, our properties file doesn't exist yet... create one!
_save();
}
try {
FileInputStream fis = new FileInputStream(this.propertiesFile);
this.properties.load(fis);
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// oops!
System.err.println("Properties cannot load!");
e.printStackTrace();
}
}
private
void _save() {
try {
FileOutputStream fos = new FileOutputStream(this.propertiesFile);
this.properties.store(fos, this.comments);
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
System.err.println("Properties cannot save!");
} catch (IOException e) {
// oops!
System.err.println("Properties cannot save!");
e.printStackTrace();
}
}
public final synchronized
void remove(final String key) {
this.properties.remove(key);
_save();
}
@SuppressWarnings("AutoBoxing")
public final synchronized
void save(final String key, Object value) {
if (key == null || value == null) {
return;
}
if (value instanceof Color) {
value = ((Color) value).getRGB();
}
this.properties.setProperty(key, value.toString());
_save();
}
@SuppressWarnings({"unchecked", "AutoUnboxing"})
public synchronized
<T> T get(String key, Class<T> clazz) {
if (key == null || clazz == null) {
return null;
}
String property = this.properties.getProperty(key);
if (property == null) {
return null;
}
// special cases
try {
if (clazz.equals(Integer.class)) {
return (T) Integer.valueOf(Integer.parseInt(property));
}
if (clazz.equals(Long.class)) {
return (T) Long.valueOf(Long.parseLong(property));
}
if (clazz.equals(Color.class)) {
return (T) new Color(Integer.parseInt(property), true);
}
else {
return (T) property;
}
} catch (Exception e) {
throw new RuntimeException("Properties Loader for property: " + key + System.getProperty("line.separator") + e.getMessage());
}
}
@Override
public
String toString() {
return "PropertiesProvider [" + this.propertiesFile + "]";
}
}

View File

@ -0,0 +1,135 @@
/*
* Copyright 2023 dorkbox, llc
*
* 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.
*/
package dorkbox.util.properties
import java.awt.Color
import java.io.*
import java.util.*
class PropertiesProvider(propertiesFile: File) {
private val properties: Properties = SortedProperties()
private val propertiesFile: File
private var comments = "Settings and configuration file. Strings must be escape formatted!"
constructor(propertiesFile: String) : this(File(propertiesFile))
init {
@Suppress("NAME_SHADOWING")
val propertiesFile = propertiesFile.normalize()
// make sure the parent dir exists...
val parentFile = propertiesFile.parentFile
if (parentFile != null && !parentFile.exists()) {
if (!parentFile.mkdirs()) {
throw RuntimeException("Unable to create directories for: $propertiesFile")
}
}
this.propertiesFile = propertiesFile
_load()
}
fun setComments(comments: String) {
this.comments = comments
}
private fun _load() {
if (!propertiesFile.canRead() || !propertiesFile.exists()) {
// in this case, our properties file doesn't exist yet... create one!
_save()
}
try {
val fis = FileInputStream(propertiesFile)
properties.load(fis)
fis.close()
} catch (e: FileNotFoundException) {
e.printStackTrace()
} catch (e: IOException) {
// oops!
System.err.println("Properties cannot load!")
e.printStackTrace()
}
}
private fun _save() {
try {
val fos = FileOutputStream(propertiesFile)
properties.store(fos, comments)
fos.flush()
fos.close()
} catch (e: FileNotFoundException) {
e.printStackTrace()
System.err.println("Properties cannot save!")
} catch (e: IOException) {
// oops!
System.err.println("Properties cannot save!")
e.printStackTrace()
}
}
@Synchronized
fun remove(key: String) {
properties.remove(key)
_save()
}
@Synchronized
fun save(key: String?, value: Any?) {
@Suppress("NAME_SHADOWING")
var value = value
if (key == null || value == null) {
return
}
if (value is Color) {
value = value.rgb
}
properties.setProperty(key, value.toString())
_save()
}
@Suppress("UNCHECKED_CAST")
@Synchronized
operator fun <T> get(key: String?, clazz: Class<T>?): T? {
if (key == null || clazz == null) {
return null
}
val property = properties.getProperty(key) ?: return null
// special cases
return try {
if (clazz == Int::class.java) {
return Integer.valueOf(property.toInt()) as T
}
if (clazz == Long::class.java) {
return java.lang.Long.valueOf(property.toLong()) as T
}
if (clazz == Color::class.java) {
Color(property.toInt(), true) as T
} else {
property as T
}
} catch (e: Exception) {
throw RuntimeException("Properties Loader for property: " + key + System.getProperty("line.separator") + e.message)
}
}
override fun toString(): String {
return "PropertiesProvider [" + propertiesFile + "]"
}
}

View File

@ -1,48 +0,0 @@
/*
* Copyright 2010 dorkbox, llc
*
* 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.
*/
package dorkbox.util.properties;
import java.util.*;
public
class SortedProperties extends Properties {
private static final long serialVersionUID = 3988064683926999433L;
private final Comparator<Object> compare = new Comparator<Object>() {
@Override
public
int compare(Object o1, Object o2) {
return o1.toString()
.compareTo(o2.toString());
}
};
@Override
public synchronized
Enumeration<Object> keys() {
Enumeration<Object> keysEnum = super.keys();
Vector<Object> vector = new Vector<Object>(size());
for (; keysEnum.hasMoreElements(); ) {
vector.add(keysEnum.nextElement());
}
Collections.sort(vector, this.compare);
return vector.elements();
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2023 dorkbox, llc
*
* 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.
*/
package dorkbox.util.properties
import dorkbox.util.Sys
import java.util.*
class SortedProperties : Properties() {
private val compare = Comparator<Any> { o1, o2 ->
o1.toString().compareTo(o2.toString())
}
@Synchronized
override fun keys(): Enumeration<Any> {
val keysEnum = super.keys()
val vector: Vector<Any> = Vector<Any>(this.size)
while (keysEnum.hasMoreElements()) {
vector.add(keysEnum.nextElement())
}
Collections.sort(vector, compare)
return vector.elements()
}
companion object {
private const val serialVersionUID = 3988064683926999433L
/**
* Gets the version number.
*/
val version = Sys.version
}
}

View File

@ -32,6 +32,7 @@
package dorkbox.util.sync
import dorkbox.util.Sys
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.withTimeout
@ -41,6 +42,13 @@ import java.util.concurrent.*
* @param initialCount this is the initial count specified when the latch was created
*/
open class AbstractLatch(val initialCount: Int, val trigger: Trigger) : Deferred<Unit> by trigger {
companion object {
/**
* Gets the version number.
*/
val version = Sys.version
}
/**
* The current latch count affected by the count*() methods.
*

View File

@ -1,24 +0,0 @@
/*
* Copyright 2021 dorkbox, llc
*
* 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.
*/
package dorkbox.util.classes;
/**
* Required for intellij to not complain regarding `module-info` for a multi-release jar.
* This file is completely ignored by the gradle build process
*/
public
class EmptyClass {}

View File

@ -2,9 +2,6 @@ module dorkbox.utilities {
exports dorkbox.exit;
exports dorkbox.urlHandler;
exports dorkbox.util;
exports dorkbox.util.classes;
exports dorkbox.util.crypto;
exports dorkbox.util.crypto.signers;
exports dorkbox.util.entropy;
exports dorkbox.util.exceptions;
exports dorkbox.util.gwt;
@ -12,7 +9,6 @@ module dorkbox.utilities {
exports dorkbox.util.sync;
exports dorkbox.util.userManagement;
requires transitive dorkbox.executor;
requires transitive dorkbox.updates;
requires transitive dorkbox.os;

View File

@ -1,277 +0,0 @@
/*
* Copyright 2015 dorkbox, llc
*
* 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.
*/
package dorkbox.util;
import java.io.IOException;
import java.util.Random;
import org.junit.Test;
public class MersenneTwisterFastTest {
@Test
public void mersenneTwisterTest() throws IOException {
int j;
MersenneTwisterFast r;
// CORRECTNESS TEST
// COMPARE WITH
// http://www.math.keio.ac.jp/matumoto/CODES/MT2002/mt19937ar.out
r = new MersenneTwisterFast(new int[] {0x123,0x234,0x345,0x456});
// System.out.println("Output of MersenneTwisterFast with new (2002/1/26) seeding mechanism");
for (j = 0; j < 1000; j++) {
// first, convert the int from signed to "unsigned"
long l = r.nextInt();
if (l < 0) {
l += 4294967296L; // max int value
}
String s = String.valueOf(l);
while (s.length() < 10) {
s = " " + s; // buffer
}
System.out.print(s + " ");
if (j % 5 == 4) {
System.out.println();
}
}
// SPEED TEST
final long SEED = 4357;
int xx;
long ms;
System.out.println("\nTime to test grabbing 100000000 ints");
Random rr = new Random(SEED);
xx = 0;
ms = System.currentTimeMillis();
for (j = 0; j < 100000000; j++) {
xx += rr.nextInt();
}
System.out.println("java.util.Random: " + (System.currentTimeMillis() - ms) + " Ignore this: " + xx);
r = new MersenneTwisterFast(SEED);
ms = System.currentTimeMillis();
xx = 0;
for (j = 0; j < 100000000; j++) {
xx += r.nextInt();
}
System.out.println("Mersenne Twister Fast: " + (System.currentTimeMillis() - ms) + " Ignore this: "
+ xx);
// TEST TO COMPARE TYPE CONVERSION BETWEEN
// MersenneTwisterFast.java AND MersenneTwister.java
boolean test = false;
System.out.println("\nGrab the first 1000 booleans");
ms = System.currentTimeMillis();
r = new MersenneTwisterFast(SEED);
for (j = 0; j < 1000; j++) {
// System.out.print(r.nextBoolean() + " ");
test = r.nextBoolean();
if (j % 8 == 7) {
// System.out.println();
test = false;
}
}
if (!(j % 8 == 7)) {
// System.out.println();
test = true;
}
System.out.println("Mersenne Twister Fast: " + (System.currentTimeMillis() - ms) + " Ignore this: "
+ xx + "" + test);
System.out.println("\nGrab 1000 booleans of increasing probability using nextBoolean(double)");
r = new MersenneTwisterFast(SEED);
ms = System.currentTimeMillis();
for (j = 0; j < 1000; j++) {
// System.out.print(r.nextBoolean(j / 999.0) + " ");
test = r.nextBoolean(j / 999.0);
if (j % 8 == 7) {
// System.out.println();
test = false;
}
}
if (!(j % 8 == 7)) {
// System.out.println();
test = true;
}
System.out.println("Mersenne Twister Fast: " + (System.currentTimeMillis() - ms) + " Ignore this: "
+ xx + "" + test);
System.out.println("\nGrab 1000 booleans of increasing probability using nextBoolean(float)");
r = new MersenneTwisterFast(SEED);
ms = System.currentTimeMillis();
for (j = 0; j < 1000; j++) {
// System.out.print(r.nextBoolean(j / 999.0f) + " ");
test = r.nextBoolean(j / 999.0f);
if (j % 8 == 7) {
test = false;
System.out.println();
}
}
if (!(j % 8 == 7)) {
// System.out.println();
test = true;
}
System.out.println("Mersenne Twister Fast: " + (System.currentTimeMillis() - ms) + " Ignore this: "
+ xx + "" + test);
byte[] bytes = new byte[1000];
System.out.println("\nGrab the first 1000 bytes using nextBytes");
r = new MersenneTwisterFast(SEED);
r.nextBytes(bytes);
for (j = 0; j < 1000; j++) {
System.out.print(bytes[j] + " ");
if (j % 16 == 15) {
System.out.println();
}
}
if (!(j % 16 == 15)) {
System.out.println();
}
byte b;
System.out.println("\nGrab the first 1000 bytes -- must be same as nextBytes");
r = new MersenneTwisterFast(SEED);
for (j = 0; j < 1000; j++) {
System.out.print((b = r.nextByte()) + " ");
if (b != bytes[j]) {
System.out.print("BAD ");
}
if (j % 16 == 15) {
System.out.println();
}
}
if (!(j % 16 == 15)) {
System.out.println();
}
System.out.println("\nGrab the first 1000 shorts");
r = new MersenneTwisterFast(SEED);
for (j = 0; j < 1000; j++) {
System.out.print(r.nextShort() + " ");
if (j % 8 == 7) {
System.out.println();
}
}
if (!(j % 8 == 7)) {
System.out.println();
}
System.out.println("\nGrab the first 1000 ints");
r = new MersenneTwisterFast(SEED);
for (j = 0; j < 1000; j++) {
System.out.print(r.nextInt() + " ");
if (j % 4 == 3) {
System.out.println();
}
}
if (!(j % 4 == 3)) {
System.out.println();
}
System.out.println("\nGrab the first 1000 ints of different sizes");
r = new MersenneTwisterFast(SEED);
int max = 1;
for (j = 0; j < 1000; j++) {
System.out.print(r.nextInt(max) + " ");
max *= 2;
if (max <= 0) {
max = 1;
}
if (j % 4 == 3) {
System.out.println();
}
}
if (!(j % 4 == 3)) {
System.out.println();
}
System.out.println("\nGrab the first 1000 longs");
r = new MersenneTwisterFast(SEED);
for (j = 0; j < 1000; j++) {
System.out.print(r.nextLong() + " ");
if (j % 3 == 2) {
System.out.println();
}
}
if (!(j % 3 == 2)) {
System.out.println();
}
System.out.println("\nGrab the first 1000 longs of different sizes");
r = new MersenneTwisterFast(SEED);
long max2 = 1;
for (j = 0; j < 1000; j++) {
System.out.print(r.nextLong(max2) + " ");
max2 *= 2;
if (max2 <= 0) {
max2 = 1;
}
if (j % 4 == 3) {
System.out.println();
}
}
if (!(j % 4 == 3)) {
System.out.println();
}
System.out.println("\nGrab the first 1000 floats");
r = new MersenneTwisterFast(SEED);
for (j = 0; j < 1000; j++) {
System.out.print(r.nextFloat() + " ");
if (j % 4 == 3) {
System.out.println();
}
}
if (!(j % 4 == 3)) {
System.out.println();
}
System.out.println("\nGrab the first 1000 doubles");
r = new MersenneTwisterFast(SEED);
for (j = 0; j < 1000; j++) {
System.out.print(r.nextDouble() + " ");
if (j % 3 == 2) {
System.out.println();
}
}
if (!(j % 3 == 2)) {
System.out.println();
}
System.out.println("\nGrab the first 1000 gaussian doubles");
r = new MersenneTwisterFast(SEED);
for (j = 0; j < 1000; j++) {
System.out.print(r.nextGaussian() + " ");
if (j % 3 == 2) {
System.out.println();
}
}
if (!(j % 3 == 2)) {
System.out.println();
}
}
}

View File

@ -0,0 +1,292 @@
/*
* Copyright 2015 dorkbox, llc
*
* 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.
*/
package dorkbox.util
import org.junit.Test
import java.io.IOException
import java.util.*
class MersenneTwisterFastTest {
@Test
@Throws(IOException::class)
fun mersenneTwisterTest() {
var j: Int
var r: MersenneTwisterFast
// CORRECTNESS TEST
// COMPARE WITH
// http://www.math.keio.ac.jp/matumoto/CODES/MT2002/mt19937ar.out
r = MersenneTwisterFast(intArrayOf(0x123, 0x234, 0x345, 0x456))
// System.out.println("Output of MersenneTwisterFast with new (2002/1/26) seeding mechanism");
j = 0
while (j < 1000) {
// first, convert the int from signed to "unsigned"
var l = r.nextInt().toLong()
if (l < 0) {
l += 4294967296L // max int value
}
var s = l.toString()
while (s.length < 10) {
s = " $s" // buffer
}
print("$s ")
if (j % 5 == 4) {
println()
}
j++
}
// SPEED TEST
val SEED: Long = 4357
var xx: Int
var ms: Long
println("\nTime to test grabbing 100000000 ints")
val rr = Random(SEED)
xx = 0
ms = System.currentTimeMillis()
j = 0
while (j < 100000000) {
xx += rr.nextInt()
j++
}
println("java.util.Random: " + (System.currentTimeMillis() - ms) + " Ignore this: " + xx)
r = MersenneTwisterFast(SEED)
ms = System.currentTimeMillis()
xx = 0
j = 0
while (j < 100000000) {
xx += r.nextInt()
j++
}
println(
"Mersenne Twister Fast: " + (System.currentTimeMillis() - ms) + " Ignore this: " + xx
)
// TEST TO COMPARE TYPE CONVERSION BETWEEN
// MersenneTwisterFast.java AND MersenneTwister.java
var test = false
println("\nGrab the first 1000 booleans")
ms = System.currentTimeMillis()
r = MersenneTwisterFast(SEED)
j = 0
while (j < 1000) {
// System.out.print(r.nextBoolean() + " ");
test = r.nextBoolean()
if (j % 8 == 7) {
// System.out.println();
test = false
}
j++
}
if (j % 8 != 7) {
// System.out.println();
test = true
}
println(
"Mersenne Twister Fast: " + (System.currentTimeMillis() - ms) + " Ignore this: " + xx + "" + test
)
println("\nGrab 1000 booleans of increasing probability using nextBoolean(double)")
r = MersenneTwisterFast(SEED)
ms = System.currentTimeMillis()
j = 0
while (j < 1000) {
// System.out.print(r.nextBoolean(j / 999.0) + " ");
test = r.nextBoolean(j / 999.0)
if (j % 8 == 7) {
// System.out.println();
test = false
}
j++
}
if (j % 8 != 7) {
// System.out.println();
test = true
}
println(
"Mersenne Twister Fast: " + (System.currentTimeMillis() - ms) + " Ignore this: " + xx + "" + test
)
println("\nGrab 1000 booleans of increasing probability using nextBoolean(float)")
r = MersenneTwisterFast(SEED)
ms = System.currentTimeMillis()
j = 0
while (j < 1000) {
// System.out.print(r.nextBoolean(j / 999.0f) + " ");
test = r.nextBoolean(j / 999.0f)
if (j % 8 == 7) {
test = false
println()
}
j++
}
if (j % 8 != 7) {
// System.out.println();
test = true
}
println(
"Mersenne Twister Fast: " + (System.currentTimeMillis() - ms) + " Ignore this: " + xx + "" + test
)
val bytes = ByteArray(1000)
println("\nGrab the first 1000 bytes using nextBytes")
r = MersenneTwisterFast(SEED)
r.nextBytes(bytes)
j = 0
while (j < 1000) {
print(bytes[j].toString() + " ")
if (j % 16 == 15) {
println()
}
j++
}
if (j % 16 != 15) {
println()
}
var b: Byte
println("\nGrab the first 1000 bytes -- must be same as nextBytes")
r = MersenneTwisterFast(SEED)
j = 0
while (j < 1000) {
print(r.nextByte().also { b = it }.toString() + " ")
if (b != bytes[j]) {
print("BAD ")
}
if (j % 16 == 15) {
println()
}
j++
}
if (j % 16 != 15) {
println()
}
println("\nGrab the first 1000 shorts")
r = MersenneTwisterFast(SEED)
j = 0
while (j < 1000) {
print(r.nextShort().toString() + " ")
if (j % 8 == 7) {
println()
}
j++
}
if (j % 8 != 7) {
println()
}
println("\nGrab the first 1000 ints")
r = MersenneTwisterFast(SEED)
j = 0
while (j < 1000) {
print(r.nextInt().toString() + " ")
if (j % 4 == 3) {
println()
}
j++
}
if (j % 4 != 3) {
println()
}
println("\nGrab the first 1000 ints of different sizes")
r = MersenneTwisterFast(SEED)
var max = 1
j = 0
while (j < 1000) {
print(r.nextInt(max).toString() + " ")
max *= 2
if (max <= 0) {
max = 1
}
if (j % 4 == 3) {
println()
}
j++
}
if (j % 4 != 3) {
println()
}
println("\nGrab the first 1000 longs")
r = MersenneTwisterFast(SEED)
j = 0
while (j < 1000) {
print(r.nextLong().toString() + " ")
if (j % 3 == 2) {
println()
}
j++
}
if (j % 3 != 2) {
println()
}
println("\nGrab the first 1000 longs of different sizes")
r = MersenneTwisterFast(SEED)
var max2: Long = 1
j = 0
while (j < 1000) {
print(r.nextLong(max2).toString() + " ")
max2 *= 2
if (max2 <= 0) {
max2 = 1
}
if (j % 4 == 3) {
println()
}
j++
}
if (j % 4 != 3) {
println()
}
println("\nGrab the first 1000 floats")
r = MersenneTwisterFast(SEED)
j = 0
while (j < 1000) {
print(r.nextFloat().toString() + " ")
if (j % 4 == 3) {
println()
}
j++
}
if (j % 4 != 3) {
println()
}
println("\nGrab the first 1000 doubles")
r = MersenneTwisterFast(SEED)
j = 0
while (j < 1000) {
print(r.nextDouble().toString() + " ")
if (j % 3 == 2) {
println()
}
j++
}
if (j % 3 != 2) {
println()
}
println("\nGrab the first 1000 gaussian doubles")
r = MersenneTwisterFast(SEED)
j = 0
while (j < 1000) {
print(r.nextGaussian().toString() + " ")
if (j % 3 == 2) {
println()
}
j++
}
if (j % 3 != 2) {
println()
}
}
}

View File

@ -0,0 +1,63 @@
/*
* Copyright 2023 dorkbox, llc
*
* 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.
*/
package dorkbox.util
import org.junit.Assert
import org.junit.Test
import java.util.concurrent.*
class TimeTest {
@Test
fun time() {
TimeUnit.DAYS.toNanos(3).also {
Assert.assertEquals("3 days", Sys.getTimePrettyFull(it))
}
TimeUnit.DAYS.toNanos(30).also {
Assert.assertEquals("30 days", Sys.getTimePrettyFull(it))
}
TimeUnit.DAYS.toNanos(300).also {
Assert.assertEquals("300 days", Sys.getTimePrettyFull(it))
}
TimeUnit.DAYS.toNanos(3000).also {
Assert.assertEquals("3000 days", Sys.getTimePrettyFull(it))
}
TimeUnit.HOURS.toNanos(3).also {
Assert.assertEquals("3 hours", Sys.getTimePrettyFull(it))
}
TimeUnit.MINUTES.toNanos(3).also {
Assert.assertEquals("3 minutes", Sys.getTimePrettyFull(it))
}
TimeUnit.SECONDS.toNanos(3).also {
Assert.assertEquals("3 seconds", Sys.getTimePrettyFull(it))
}
TimeUnit.MILLISECONDS.toNanos(3).also {
Assert.assertEquals("3 milli-seconds", Sys.getTimePrettyFull(it))
}
TimeUnit.MICROSECONDS.toNanos(3).also {
Assert.assertEquals("3 micro-seconds", Sys.getTimePrettyFull(it))
}
TimeUnit.NANOSECONDS.toNanos(3).also {
Assert.assertEquals("3 nano-seconds", Sys.getTimePrettyFull(it))
}
TimeUnit.NANOSECONDS.toNanos(1).also {
Assert.assertEquals("1 nano-second", Sys.getTimePrettyFull(it))
}
}
}

View File

@ -1,343 +0,0 @@
/*
* Copyright 2015 dorkbox, llc
*
* 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.
*/
package dorkbox.util.crypto;
import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.Arrays;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.engines.AESFastEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.junit.Test;
public class AesTest {
org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(this.getClass());
private static String entropySeed = "asdjhasdkljalksdfhlaks4356268909087s0dfgkjh255124515hasdg87";
@Test
public void AesGcm() throws IOException {
byte[] bytes = "hello, my name is inigo montoya".getBytes();
SecureRandom rand = new SecureRandom(entropySeed.getBytes());
GCMBlockCipher aesEngine = new GCMBlockCipher(new AESEngine());
byte[] key = new byte[32];
byte[] iv = new byte[16];
// note: the IV needs to be VERY unique!
rand.nextBytes(key); // 256bit key (32 bytes)
rand.nextBytes(iv); // 128bit block size (16 bytes)
byte[] encryptAES = CryptoAES.encrypt(aesEngine, key, iv, bytes, logger);
byte[] decryptAES = CryptoAES.decrypt(aesEngine, key, iv, encryptAES, logger);
if (Arrays.equals(bytes, encryptAES)) {
fail("bytes should not be equal");
}
if (!Arrays.equals(bytes, decryptAES)) {
fail("bytes not equal");
}
}
// Note: this is still tested, but DO NOT USE BLOCK MODE as it does NOT provide authentication. GCM does.
@SuppressWarnings("deprecation")
@Test
public void AesBlock() throws IOException {
byte[] bytes = "hello, my name is inigo montoya".getBytes();
SecureRandom rand = new SecureRandom(entropySeed.getBytes());
PaddedBufferedBlockCipher aesEngine = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESFastEngine()));
byte[] key = new byte[32];
byte[] iv = new byte[16];
// note: the IV needs to be VERY unique!
rand.nextBytes(key); // 256bit key
rand.nextBytes(iv); // 16bit block size
byte[] encryptAES = CryptoAES.encrypt(aesEngine, key, iv, bytes, logger);
byte[] decryptAES = CryptoAES.decrypt(aesEngine, key, iv, encryptAES, logger);
if (Arrays.equals(bytes, encryptAES)) {
fail("bytes should not be equal");
}
if (!Arrays.equals(bytes, decryptAES)) {
fail("bytes not equal");
}
}
@Test
public void AesGcmStream() throws IOException {
byte[] originalBytes = "hello, my name is inigo montoya".getBytes();
ByteArrayInputStream inputStream = new ByteArrayInputStream(originalBytes);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
SecureRandom rand = new SecureRandom(entropySeed.getBytes());
GCMBlockCipher aesEngine = new GCMBlockCipher(new AESEngine());
byte[] key = new byte[32];
byte[] iv = new byte[16];
// note: the IV needs to be VERY unique!
rand.nextBytes(key); // 256bit key
rand.nextBytes(iv); // 128bit block size
boolean success = CryptoAES.encryptStream(aesEngine, key, iv, inputStream, outputStream, logger);
if (!success) {
fail("crypto was not successful");
}
byte[] encryptBytes = outputStream.toByteArray();
inputStream = new ByteArrayInputStream(outputStream.toByteArray());
outputStream = new ByteArrayOutputStream();
success = CryptoAES.decryptStream(aesEngine, key, iv, inputStream, outputStream, logger);
if (!success) {
fail("crypto was not successful");
}
byte[] decryptBytes = outputStream.toByteArray();
if (Arrays.equals(originalBytes, encryptBytes)) {
fail("bytes should not be equal");
}
if (!Arrays.equals(originalBytes, decryptBytes)) {
fail("bytes not equal");
}
}
// Note: this is still tested, but DO NOT USE BLOCK MODE as it does NOT provide authentication. GCM does.
@SuppressWarnings("deprecation")
@Test
public void AesBlockStream() throws IOException {
byte[] originalBytes = "hello, my name is inigo montoya".getBytes();
ByteArrayInputStream inputStream = new ByteArrayInputStream(originalBytes);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
SecureRandom rand = new SecureRandom(entropySeed.getBytes());
PaddedBufferedBlockCipher aesEngine = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESFastEngine()));
byte[] key = new byte[32];
byte[] iv = new byte[16];
// note: the IV needs to be VERY unique!
rand.nextBytes(key); // 256bit key
rand.nextBytes(iv); // 128bit block size
boolean success = CryptoAES.encryptStream(aesEngine, key, iv, inputStream, outputStream, logger);
if (!success) {
fail("crypto was not successful");
}
byte[] encryptBytes = outputStream.toByteArray();
inputStream = new ByteArrayInputStream(outputStream.toByteArray());
outputStream = new ByteArrayOutputStream();
success = CryptoAES.decryptStream(aesEngine, key, iv, inputStream, outputStream, logger);
if (!success) {
fail("crypto was not successful");
}
byte[] decryptBytes = outputStream.toByteArray();
if (Arrays.equals(originalBytes, encryptBytes)) {
fail("bytes should not be equal");
}
if (!Arrays.equals(originalBytes, decryptBytes)) {
fail("bytes not equal");
}
}
@Test
public void AesWithIVGcm() throws IOException {
byte[] bytes = "hello, my name is inigo montoya".getBytes();
SecureRandom rand = new SecureRandom(entropySeed.getBytes());
GCMBlockCipher aesEngine = new GCMBlockCipher(new AESEngine());
byte[] key = new byte[32]; // 256bit key
byte[] iv = new byte[aesEngine.getUnderlyingCipher().getBlockSize()];
// note: the IV needs to be VERY unique!
rand.nextBytes(key);
rand.nextBytes(iv);
byte[] encryptAES = CryptoAES.encryptWithIV(aesEngine, key, iv, bytes, logger);
byte[] decryptAES = CryptoAES.decryptWithIV(aesEngine, key, encryptAES, logger);
if (Arrays.equals(bytes, encryptAES)) {
fail("bytes should not be equal");
}
if (!Arrays.equals(bytes, decryptAES)) {
fail("bytes not equal");
}
}
// Note: this is still tested, but DO NOT USE BLOCK MODE as it does NOT provide authentication. GCM does.
@SuppressWarnings("deprecation")
@Test
public void AesWithIVBlock() throws IOException {
byte[] bytes = "hello, my name is inigo montoya".getBytes();
SecureRandom rand = new SecureRandom(entropySeed.getBytes());
PaddedBufferedBlockCipher aesEngine = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESFastEngine()));
byte[] key = new byte[32]; // 256bit key
byte[] iv = new byte[aesEngine.getUnderlyingCipher().getBlockSize()];
// note: the IV needs to be VERY unique!
rand.nextBytes(key);
rand.nextBytes(iv);
byte[] encryptAES = CryptoAES.encryptWithIV(aesEngine, key, iv, bytes, logger);
byte[] decryptAES = CryptoAES.decryptWithIV(aesEngine, key, encryptAES, logger);
if (Arrays.equals(bytes, encryptAES)) {
fail("bytes should not be equal");
}
if (!Arrays.equals(bytes, decryptAES)) {
fail("bytes not equal");
}
}
@Test
public void AesWithIVGcmStream() throws IOException {
byte[] originalBytes = "hello, my name is inigo montoya".getBytes();
ByteArrayInputStream inputStream = new ByteArrayInputStream(originalBytes);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
SecureRandom rand = new SecureRandom(entropySeed.getBytes());
GCMBlockCipher aesEngine = new GCMBlockCipher(new AESEngine());
byte[] key = new byte[32];
byte[] iv = new byte[16];
// note: the IV needs to be VERY unique!
rand.nextBytes(key); // 256bit key
rand.nextBytes(iv); // 128bit block size
boolean success = CryptoAES.encryptStreamWithIV(aesEngine, key, iv, inputStream, outputStream, logger);
if (!success) {
fail("crypto was not successful");
}
byte[] encryptBytes = outputStream.toByteArray();
inputStream = new ByteArrayInputStream(outputStream.toByteArray());
outputStream = new ByteArrayOutputStream();
success = CryptoAES.decryptStreamWithIV(aesEngine, key, inputStream, outputStream, logger);
if (!success) {
fail("crypto was not successful");
}
byte[] decryptBytes = outputStream.toByteArray();
if (Arrays.equals(originalBytes, encryptBytes)) {
fail("bytes should not be equal");
}
if (!Arrays.equals(originalBytes, decryptBytes)) {
fail("bytes not equal");
}
}
// Note: this is still tested, but DO NOT USE BLOCK MODE as it does NOT provide authentication. GCM does.
@SuppressWarnings("deprecation")
@Test
public void AesWithIVBlockStream() throws IOException {
byte[] originalBytes = "hello, my name is inigo montoya".getBytes();
ByteArrayInputStream inputStream = new ByteArrayInputStream(originalBytes);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
SecureRandom rand = new SecureRandom(entropySeed.getBytes());
PaddedBufferedBlockCipher aesEngine = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESFastEngine()));
byte[] key = new byte[32];
byte[] iv = new byte[16];
// note: the IV needs to be VERY unique!
rand.nextBytes(key); // 256bit key
rand.nextBytes(iv); // 128bit block size
boolean success = CryptoAES.encryptStreamWithIV(aesEngine, key, iv, inputStream, outputStream, logger);
if (!success) {
fail("crypto was not successful");
}
byte[] encryptBytes = outputStream.toByteArray();
inputStream = new ByteArrayInputStream(outputStream.toByteArray());
outputStream = new ByteArrayOutputStream();
success = CryptoAES.decryptStreamWithIV(aesEngine, key, inputStream, outputStream, logger);
if (!success) {
fail("crypto was not successful");
}
byte[] decryptBytes = outputStream.toByteArray();
if (Arrays.equals(originalBytes, encryptBytes)) {
fail("bytes should not be equal");
}
if (!Arrays.equals(originalBytes, decryptBytes)) {
fail("bytes not equal");
}
}
}

View File

@ -1,148 +0,0 @@
/*
* Copyright 2015 dorkbox, llc
*
* 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.
*/
package dorkbox.util.crypto;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Arrays;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.asn1.x509.DSAParameter;
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.DSAParameters;
import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.crypto.util.PublicKeyFactory;
import org.junit.Test;
@SuppressWarnings("deprecation")
public class DsaTest {
private static String entropySeed = "asdjhaffasttjjhgpx600gn,-356268909087s0dfgkjh255124515hasdg87";
// Note: this is here just for keeping track of how this is done. This should NOT be used, and instead ECC crypto used instead.
@Test
public void Dsa() {
byte[] bytes = "hello, my name is inigo montoya".getBytes();
AsymmetricCipherKeyPair generateKeyPair = CryptoDSA.generateKeyPair(new SecureRandom(entropySeed.getBytes()), 1024);
DSAPrivateKeyParameters privateKey = (DSAPrivateKeyParameters) generateKeyPair.getPrivate();
DSAPublicKeyParameters publicKey = (DSAPublicKeyParameters) generateKeyPair.getPublic();
BigInteger[] signature = CryptoDSA.generateSignature(privateKey, new SecureRandom(entropySeed.getBytes()), bytes);
boolean verify1 = CryptoDSA.verifySignature(publicKey, bytes, signature);
if (!verify1) {
fail("failed signature verification");
}
byte[] bytes2 = "hello, my name is inigo montoya FAILED VERSION".getBytes();
if (Arrays.equals(bytes, bytes2)) {
fail("failed to create different byte arrays for testing bad messages");
}
boolean verify2 = CryptoDSA.verifySignature(publicKey, bytes2, signature);
if (verify2) {
fail("failed signature verification with bad message");
}
}
@Test
public void DsaJceSerializaion() throws IOException {
AsymmetricCipherKeyPair generateKeyPair = CryptoDSA.generateKeyPair(new SecureRandom(entropySeed.getBytes()), 1024);
DSAPrivateKeyParameters privateKey = (DSAPrivateKeyParameters) generateKeyPair.getPrivate();
DSAPublicKeyParameters publicKey = (DSAPublicKeyParameters) generateKeyPair.getPublic();
// public key as bytes.
DSAParameters parameters = publicKey.getParameters();
byte[] bs = new SubjectPublicKeyInfo(
new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa,
new DSAParameter(parameters.getP(), parameters.getQ(), parameters.getG()).toASN1Primitive()),
new ASN1Integer(publicKey.getY())).getEncoded();
parameters = privateKey.getParameters();
byte[] bs2 = new PrivateKeyInfo(
new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa,
new DSAParameter(parameters.getP(), parameters.getQ(), parameters.getG()).toASN1Primitive()),
new ASN1Integer(privateKey.getX())).getEncoded();
DSAPrivateKeyParameters privateKey2 = (DSAPrivateKeyParameters) PrivateKeyFactory.createKey(bs2);
DSAPublicKeyParameters publicKey2 = (DSAPublicKeyParameters) PublicKeyFactory.createKey(bs);
// test via signing
byte[] bytes = "hello, my name is inigo montoya".getBytes();
BigInteger[] signature = CryptoDSA.generateSignature(privateKey, new SecureRandom(entropySeed.getBytes()), bytes);
boolean verify1 = CryptoDSA.verifySignature(publicKey, bytes, signature);
if (!verify1) {
fail("failed signature verification");
}
boolean verify2 = CryptoDSA.verifySignature(publicKey2, bytes, signature);
if (!verify2) {
fail("failed signature verification");
}
// now reverse who signs what.
BigInteger[] signatureB = CryptoDSA.generateSignature(privateKey2, new SecureRandom(entropySeed.getBytes()), bytes);
boolean verifyB1 = CryptoDSA.verifySignature(publicKey, bytes, signatureB);
if (!verifyB1) {
fail("failed signature verification");
}
boolean verifyB2 = CryptoDSA.verifySignature(publicKey2, bytes, signatureB);
if (!verifyB2) {
fail("failed signature verification");
}
}
}

View File

@ -1,332 +0,0 @@
/*
* Copyright 2015 dorkbox, llc
*
* 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.
*/
package dorkbox.util.crypto;
import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Arrays;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.BasicAgreement;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.agreement.ECDHCBasicAgreement;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.engines.IESEngine;
import org.bouncycastle.crypto.modes.CBCBlockCipher;
import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.IESParameters;
import org.bouncycastle.crypto.params.IESWithCipherParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.crypto.signers.ECDSASigner;
import org.bouncycastle.crypto.util.PrivateKeyFactory;
import org.bouncycastle.crypto.util.PublicKeyFactory;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.util.encoders.Hex;
import org.junit.Test;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import dorkbox.serializers.bouncycastle.EccPrivateKeySerializer;
import dorkbox.serializers.bouncycastle.EccPublicKeySerializer;
import dorkbox.serializers.bouncycastle.IesParametersSerializer;
import dorkbox.serializers.bouncycastle.IesWithCipherParametersSerializer;
public class EccTest {
org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(this.getClass());
private static String entropySeed = "asdjhaffasttjasdasdgfgaerym0698768.,./8909087s0dfgkjgb49bmngrSGDSG#";
@Test
public void EccStreamMode() throws IOException {
SecureRandom secureRandom = new SecureRandom();
AsymmetricCipherKeyPair key1 = CryptoECC.generateKeyPair(CryptoECC.default_curve, secureRandom);
AsymmetricCipherKeyPair key2 = CryptoECC.generateKeyPair(CryptoECC.default_curve, secureRandom);
IESParameters cipherParams = CryptoECC.generateSharedParameters(secureRandom);
IESEngine encrypt = CryptoECC.createEngine();
IESEngine decrypt = CryptoECC.createEngine();
// note: we want an ecc key that is AT LEAST 512 bits! (which is equal to AES 256)
// using 521 bits from curve.
CipherParameters private1 = key1.getPrivate();
CipherParameters public1 = key1.getPublic();
CipherParameters private2 = key2.getPrivate();
CipherParameters public2 = key2.getPublic();
byte[] message = Hex.decode(
"123456784358754934597967249867359283792374987692348750276509765091834790abcdef123456784358754934597967249867359283792374987692348750276509765091834790abcdef123456784358754934597967249867359283792374987692348750276509765091834790abcdef");
// test stream mode
byte[] encrypted = CryptoECC.encrypt(encrypt, private1, public2, cipherParams, message, logger);
byte[] plaintext = CryptoECC.decrypt(decrypt, private2, public1, cipherParams, encrypted, logger);
if (Arrays.equals(encrypted, message)) {
fail("stream cipher test failed");
}
if (!Arrays.equals(plaintext, message)) {
fail("stream cipher test failed");
}
}
@Test
public void EccAesMode() throws IOException {
// test AES encrypt mode
SecureRandom secureRandom = new SecureRandom();
AsymmetricCipherKeyPair key1 = CryptoECC.generateKeyPair(CryptoECC.default_curve, secureRandom);
AsymmetricCipherKeyPair key2 = CryptoECC.generateKeyPair(CryptoECC.default_curve, secureRandom);
PaddedBufferedBlockCipher aesEngine1 = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
PaddedBufferedBlockCipher aesEngine2 = new PaddedBufferedBlockCipher(new CBCBlockCipher(new AESEngine()));
IESWithCipherParameters cipherParams = CryptoECC.generateSharedParametersWithCipher(secureRandom);
IESEngine encrypt = CryptoECC.createEngine(aesEngine1);
IESEngine decrypt = CryptoECC.createEngine(aesEngine2);
// note: we want an ecc key that is AT LEAST 512 bits! (which is equal to AES 256)
// using 521 bits from curve.
CipherParameters private1 = key1.getPrivate();
CipherParameters public1 = key1.getPublic();
CipherParameters private2 = key2.getPrivate();
CipherParameters public2 = key2.getPublic();
byte[] message = Hex.decode("123456784358754934597967249867359283792374987692348750276509765091834790abcdef123456784358754934597967249867359283792374987692348750276509765091834790abcdef123456784358754934597967249867359283792374987692348750276509765091834790abcdef");
// test stream mode
byte[] encrypted = CryptoECC.encrypt(encrypt, private1, public2, cipherParams, message, logger);
byte[] plaintext = CryptoECC.decrypt(decrypt, private2, public1, cipherParams, encrypted, logger);
if (Arrays.equals(encrypted, message)) {
fail("stream cipher test failed");
}
if (!Arrays.equals(plaintext, message)) {
fail("stream cipher test failed");
}
}
@Test
public void Ecdh() throws IOException {
// test DH key exchange
SecureRandom secureRandom = new SecureRandom();
AsymmetricCipherKeyPair key1 = CryptoECC.generateKeyPair(CryptoECC.default_curve, secureRandom);
AsymmetricCipherKeyPair key2 = CryptoECC.generateKeyPair(CryptoECC.default_curve, secureRandom);
BasicAgreement e1 = new ECDHCBasicAgreement();
BasicAgreement e2 = new ECDHCBasicAgreement();
e1.init(key1.getPrivate());
e2.init(key2.getPrivate());
BigInteger k1 = e1.calculateAgreement(key2.getPublic());
BigInteger k2 = e2.calculateAgreement(key1.getPublic());
if (!k1.equals(k2)) {
fail("ECDHC cipher test failed");
}
}
@Test
public void EccDsa() throws IOException {
SecureRandom secureRandom = new SecureRandom();
AsymmetricCipherKeyPair key1 = CryptoECC.generateKeyPair(CryptoECC.default_curve, secureRandom);
ParametersWithRandom param = new ParametersWithRandom(key1.getPrivate(), new SecureRandom());
ECDSASigner ecdsa = new ECDSASigner();
ecdsa.init(true, param);
byte[] message = new BigInteger("345234598734987394672039478602934578").toByteArray();
BigInteger[] sig = ecdsa.generateSignature(message);
ecdsa.init(false, key1.getPublic());
if (!ecdsa.verifySignature(message, sig[0], sig[1])) {
fail("ECDSA signature fails");
}
}
@Test
public void EccSerialization() {
SecureRandom secureRandom = new SecureRandom();
AsymmetricCipherKeyPair key1 = CryptoECC.generateKeyPair(CryptoECC.default_curve, secureRandom);
IESParameters cipherAParams = CryptoECC.generateSharedParameters(secureRandom);
IESWithCipherParameters cipherBParams = CryptoECC.generateSharedParametersWithCipher(secureRandom);
// note: we want an ecc key that is AT LEAST 512 bits! (which is equal to AES 256)
// using 521 bits from curve.
ECPrivateKeyParameters private1 = (ECPrivateKeyParameters) key1.getPrivate();
ECPublicKeyParameters public1 = (ECPublicKeyParameters) key1.getPublic();
Kryo kryo = new Kryo();
kryo.register(IESParameters.class, new IesParametersSerializer());
kryo.register(IESWithCipherParameters.class, new IesWithCipherParametersSerializer());
kryo.register(ECPublicKeyParameters.class, new EccPublicKeySerializer());
kryo.register(ECPrivateKeyParameters.class, new EccPrivateKeySerializer());
// Test output to stream, large buffer.
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
Output output = new Output(outStream, 4096);
kryo.writeClassAndObject(output, cipherAParams);
output.flush();
// Test input from stream, large buffer.
Input input = new Input(new ByteArrayInputStream(outStream.toByteArray()), 4096);
IESParameters cipherAParams2 = (IESParameters) kryo.readClassAndObject(input);
if (!CryptoECC.compare(cipherAParams, cipherAParams2)) {
fail("cipher parameters not equal");
}
// Test output to stream, large buffer.
outStream = new ByteArrayOutputStream();
output = new Output(outStream, 4096);
kryo.writeClassAndObject(output, cipherBParams);
output.flush();
// Test input from stream, large buffer.
input = new Input(new ByteArrayInputStream(outStream.toByteArray()), 4096);
IESWithCipherParameters cipherBParams2 = (IESWithCipherParameters) kryo.readClassAndObject(input);
if (!CryptoECC.compare(cipherBParams, cipherBParams2)) {
fail("cipher parameters not equal");
}
// Test output to stream, large buffer.
outStream = new ByteArrayOutputStream();
output = new Output(outStream, 4096);
kryo.writeClassAndObject(output, private1);
output.flush();
// Test input from stream, large buffer.
input = new Input(new ByteArrayInputStream(outStream.toByteArray()), 4096);
ECPrivateKeyParameters private2 = (ECPrivateKeyParameters) kryo.readClassAndObject(input);
if (!CryptoECC.compare(private1, private2)) {
fail("private keys not equal");
}
// Test output to stream, large buffer.
outStream = new ByteArrayOutputStream();
output = new Output(outStream, 4096);
kryo.writeClassAndObject(output, public1);
output.flush();
// Test input from stream, large buffer.
input = new Input(new ByteArrayInputStream(outStream.toByteArray()), 4096);
ECPublicKeyParameters public2 = (ECPublicKeyParameters) kryo.readClassAndObject(input);
if (!CryptoECC.compare(public1, public2)) {
fail("public keys not equal");
}
}
@Test
public void EccJceSerialization() throws IOException {
AsymmetricCipherKeyPair generateKeyPair = CryptoECC.generateKeyPair(CryptoECC.default_curve, new SecureRandom());
ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) generateKeyPair.getPrivate();
ECPublicKeyParameters publicKey = (ECPublicKeyParameters) generateKeyPair.getPublic();
BCECPublicKey bcecPublicKey = new BCECPublicKey("EC", publicKey, (ECParameterSpec) null, BouncyCastleProvider.CONFIGURATION);
byte[] publicBytes = bcecPublicKey.getEncoded();
// relies on the BC public key.
BCECPrivateKey bcecPrivateKey = new BCECPrivateKey("EC", privateKey, bcecPublicKey, (ECParameterSpec) null, BouncyCastleProvider.CONFIGURATION);
byte[] privateBytes = bcecPrivateKey.getEncoded();
ECPublicKeyParameters publicKey2 = (ECPublicKeyParameters) PublicKeyFactory.createKey(publicBytes);
ECPrivateKeyParameters privateKey2 = (ECPrivateKeyParameters) PrivateKeyFactory.createKey(privateBytes);
// test via signing
byte[] bytes = "hello, my name is inigo montoya".getBytes();
BigInteger[] signature = CryptoECC.generateSignature("SHA384", privateKey, new SecureRandom(entropySeed.getBytes()), bytes);
boolean verify1 = CryptoECC.verifySignature("SHA384", publicKey, bytes, signature);
if (!verify1) {
fail("failed signature verification");
}
boolean verify2 = CryptoECC.verifySignature("SHA384", publicKey2, bytes, signature);
if (!verify2) {
fail("failed signature verification");
}
// now reverse who signs what.
BigInteger[] signatureB = CryptoECC.generateSignature("SHA384", privateKey2, new SecureRandom(entropySeed.getBytes()), bytes);
boolean verifyB1 = CryptoECC.verifySignature("SHA384", publicKey, bytes, signatureB);
if (!verifyB1) {
fail("failed signature verification");
}
boolean verifyB2 = CryptoECC.verifySignature("SHA384", publicKey2, bytes, signatureB);
if (!verifyB2) {
fail("failed signature verification");
}
}
}

View File

@ -1,171 +0,0 @@
/*
* Copyright 2015 dorkbox, llc
*
* 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.
*/
package dorkbox.util.crypto;
import java.security.SecureRandom;
import java.util.Random;
import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.engines.AESEngine;
import org.bouncycastle.crypto.modes.GCMBlockCipher;
import org.bouncycastle.crypto.params.KeyParameter;
import org.bouncycastle.crypto.params.ParametersWithIV;
// See: https://stackoverflow.com/questions/25992131/slow-aes-gcm-encryption-and-decryption-with-java-8u20
// java8 performance is 3 MB/s. BC is ~43 MB/s
public
class PerformanceTest {
private static org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(PerformanceTest.class);
private static String entropySeed = "asdjhasdkljalksdfhlaks4356268909087s0dfgkjh255124515hasdg87";
public static
void main(String[] args) throws Exception {
final int max = 5;
for (int i = 0; i < max; i++) {
System.out.println("Warming up " + (i+1) + " of " + max);
new PerformanceTest(true);
}
new PerformanceTest(false);
}
PerformanceTest(boolean isWarmup) {
final byte[] bytes = new byte[64 * 1024];
byte[] encrypted = null;
final byte[] aesKey = new byte[32];
final byte[] aesIV = new byte[12];
final Random random = new SecureRandom(entropySeed.getBytes());
random.nextBytes(bytes);
random.nextBytes(aesKey);
random.nextBytes(aesIV);
int length = bytes.length;
if (!isWarmup) {
System.out.println("Benchmarking AES-256 GCM encryption");
}
long javaEncryptInputBytes = 0;
long javaEncryptStartTime = System.currentTimeMillis();
// convert to bouncycastle
GCMBlockCipher aesEngine = new GCMBlockCipher(new AESEngine());
CipherParameters aesIVAndKey = new ParametersWithIV(new KeyParameter(aesKey), aesIV);
long encryptInitTime = 0L;
long encryptUpdate1Time = 0L;
long encryptDoFinalTime = 0L;
while (System.currentTimeMillis() - javaEncryptStartTime < 10000) {
random.nextBytes(aesIV);
long n1 = System.nanoTime();
aesEngine.reset();
aesEngine.init(true, aesIVAndKey);
if (encrypted == null) {
int minSize = aesEngine.getOutputSize(length);
encrypted = new byte[minSize];
}
long n2 = System.nanoTime();
int actualLength = aesEngine.processBytes(bytes, 0, length, encrypted, 0);
long n3 = System.nanoTime();
try {
actualLength += aesEngine.doFinal(encrypted, actualLength);
} catch (Exception e) {
logger.error("Unable to perform AES cipher.", e);
}
if (encrypted.length != actualLength) {
byte[] result = new byte[actualLength];
System.arraycopy(encrypted, 0, result, 0, result.length);
encrypted = result;
}
long n4 = System.nanoTime();
javaEncryptInputBytes += actualLength;
encryptInitTime = n2 - n1;
encryptUpdate1Time = n3 - n2;
encryptDoFinalTime = n4 - n3;
}
long javaEncryptEndTime = System.currentTimeMillis();
if (!isWarmup) {
System.out.println("Time init (ns): " + encryptInitTime);
System.out.println("Time update (ns): " + encryptUpdate1Time);
System.out.println("Time do final (ns): " + encryptDoFinalTime);
System.out.println("Java calculated at " +
(javaEncryptInputBytes / 1024 / 1024 / ((javaEncryptEndTime - javaEncryptStartTime) / 1000)) + " MB/s");
System.out.println("Benchmarking AES-256 GCM decryption");
}
long javaDecryptInputBytes = 0;
long javaDecryptStartTime = System.currentTimeMillis();
length = encrypted.length;
long decryptInitTime = 0L;
long decryptUpdate1Time = 0L;
long decryptDoFinalTime = 0L;
while (System.currentTimeMillis() - javaDecryptStartTime < 10000) {
long n1 = System.nanoTime();
aesEngine.reset();
aesEngine.init(false, aesIVAndKey);
long n2 = System.nanoTime();
int actualLength = aesEngine.processBytes(encrypted, 0, length, bytes, 0);
long n3 = System.nanoTime();
try {
actualLength += aesEngine.doFinal(bytes, actualLength);
} catch (Exception e) {
logger.debug("Unable to perform AES cipher.", e);
}
long n4 = System.nanoTime();
javaDecryptInputBytes += actualLength;
decryptInitTime += n2 - n1;
decryptUpdate1Time += n3 - n2;
decryptDoFinalTime += n4 - n3;
}
long javaDecryptEndTime = System.currentTimeMillis();
if (!isWarmup) {
System.out.println("Time init (ns): " + decryptInitTime);
System.out.println("Time update 1 (ns): " + decryptUpdate1Time);
System.out.println("Time do final (ns): " + decryptDoFinalTime);
System.out.println("Total bytes processed: " + javaDecryptInputBytes);
System.out.println("Java calculated at " +
(javaDecryptInputBytes / 1024 / 1024 / ((javaDecryptEndTime - javaDecryptStartTime) / 1000)) + " MB/s");
}
}
}

View File

@ -1,141 +0,0 @@
/*
* Copyright 2015 dorkbox, llc
*
* 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.
*/
package dorkbox.util.crypto;
import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Arrays;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.digests.SHA1Digest;
import org.bouncycastle.crypto.encodings.OAEPEncoding;
import org.bouncycastle.crypto.engines.RSAEngine;
import org.bouncycastle.crypto.generators.RSAKeyPairGenerator;
import org.bouncycastle.crypto.params.RSAKeyGenerationParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.bouncycastle.crypto.signers.PSSSigner;
import org.junit.Test;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import dorkbox.serializers.bouncycastle.RsaPrivateKeySerializer;
import dorkbox.serializers.bouncycastle.RsaPublicKeySerializer;
public class RsaTest {
org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(this.getClass());
private static String entropySeed = "asdjhaffasttjjhgpx600gn,-356268909087s0dfgkjh255124515hasdg87";
@SuppressWarnings("deprecation")
@Test
public void Rsa() {
byte[] bytes = "hello, my name is inigo montoya".getBytes();
AsymmetricCipherKeyPair key = CryptoRSA.generateKeyPair(new SecureRandom(entropySeed.getBytes()), 1024);
RSAKeyParameters public1 = (RSAKeyParameters) key.getPublic();
RSAPrivateCrtKeyParameters private1 = (RSAPrivateCrtKeyParameters) key.getPrivate();
RSAEngine engine = new RSAEngine();
SHA1Digest digest = new SHA1Digest();
OAEPEncoding rsaEngine = new OAEPEncoding(engine, digest);
// test encrypt/decrypt
byte[] encryptRSA = CryptoRSA.encrypt(rsaEngine, public1, bytes, logger);
byte[] decryptRSA = CryptoRSA.decrypt(rsaEngine, private1, encryptRSA, logger);
if (Arrays.equals(bytes, encryptRSA)) {
fail("bytes should not be equal");
}
if (!Arrays.equals(bytes, decryptRSA)) {
fail("bytes not equal");
}
// test signing/verification
PSSSigner signer = new PSSSigner(engine, digest, digest.getDigestSize());
byte[] signatureRSA = CryptoRSA.sign(signer, private1, bytes, logger);
boolean verify = CryptoRSA.verify(signer, public1, signatureRSA, bytes);
if (!verify) {
fail("failed signature verification");
}
}
@SuppressWarnings("deprecation")
@Test
public void RsaSerialization () throws IOException {
RSAKeyPairGenerator keyGen = new RSAKeyPairGenerator();
RSAKeyGenerationParameters params = new RSAKeyGenerationParameters(new BigInteger("65537"), // public exponent
new SecureRandom(entropySeed.getBytes()), //pnrg
1024, // key length
8); //the number of iterations of the Miller-Rabin primality test.
keyGen.init(params);
AsymmetricCipherKeyPair key = keyGen.generateKeyPair();
RSAKeyParameters public1 = (RSAKeyParameters) key.getPublic();
RSAPrivateCrtKeyParameters private1 = (RSAPrivateCrtKeyParameters) key.getPrivate();
Kryo kryo = new Kryo();
kryo.register(RSAKeyParameters.class, new RsaPublicKeySerializer());
kryo.register(RSAPrivateCrtKeyParameters.class, new RsaPrivateKeySerializer());
// Test output to stream, large buffer.
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
Output output = new Output(outStream, 4096);
kryo.writeClassAndObject(output, public1);
output.flush();
// Test input from stream, large buffer.
Input input = new Input(new ByteArrayInputStream(outStream.toByteArray()), 4096);
RSAKeyParameters public2 = (RSAKeyParameters) kryo.readClassAndObject(input);
if (!CryptoRSA.compare(public1, public2)) {
fail("public keys not equal");
}
// Test output to stream, large buffer.
outStream = new ByteArrayOutputStream();
output = new Output(outStream, 4096);
kryo.writeClassAndObject(output, private1);
output.flush();
// Test input from stream, large buffer.
input = new Input(new ByteArrayInputStream(outStream.toByteArray()), 4096);
RSAPrivateCrtKeyParameters private2 = (RSAPrivateCrtKeyParameters) kryo.readClassAndObject(input);
if (!CryptoRSA.compare(private1, private2)) {
fail("private keys not equal");
}
}
}

View File

@ -1,71 +0,0 @@
/*
* Copyright 2015 dorkbox, llc
*
* 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.
*/
package dorkbox.util.crypto;
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import java.security.GeneralSecurityException;
import org.junit.Test;
import dorkbox.util.Sys;
public class SCryptTest {
@Test
public void SCrypt() throws IOException, GeneralSecurityException {
byte[] P, S;
int N, r, p, dkLen;
String DK;
// empty key & salt test missing because unsupported by JCE
P = "password".getBytes("UTF-8");
S = "NaCl".getBytes("UTF-8");
N = 1024;
r = 8;
p = 16;
dkLen = 64;
DK = "FDBABE1C9D3472007856E7190D01E9FE7C6AD7CBC8237830E77376634B3731622EAF30D92E22A3886FF109279D9830DAC727AFB94A83EE6D8360CBDFA2CC0640";
assertEquals(DK, Sys.bytesToHex(CryptoSCrypt.encrypt(P, S, N, r, p, dkLen)));
P = "pleaseletmein".getBytes("UTF-8");
S = "SodiumChloride".getBytes("UTF-8");
N = 16384;
r = 8;
p = 1;
dkLen = 64;
DK = "7023BDCB3AFD7348461C06CD81FD38EBFDA8FBBA904F8E3EA9B543F6545DA1F2D5432955613F0FCF62D49705242A9AF9E61E85DC0D651E40DFCF017B45575887";
assertEquals(DK, Sys.bytesToHex(CryptoSCrypt.encrypt(P, S, N, r, p, dkLen)));
P = "pleaseletmein".getBytes("UTF-8");
S = "SodiumChloride".getBytes("UTF-8");
N = 1048576;
r = 8;
p = 1;
dkLen = 64;
DK = "2101CB9B6A511AAEADDBBE09CF70F881EC568D574A2FFD4DABE5EE9820ADAA478E56FD8F4BA5D09FFA1C6D927C40F4C337304049E8A952FBCBF45C6FA77A41A4";
assertEquals(DK, Sys.bytesToHex(CryptoSCrypt.encrypt(P, S, N, r, p, dkLen)));
}
}

View File

@ -1,188 +0,0 @@
package dorkbox.util.crypto;
// Copyright (c) 2006 Damien Miller <djm@mindrot.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import junit.framework.TestCase;
/**
* JUnit unit tests for BCrypt routines
* @author Damien Miller
* @version 0.2
*/
public class TestBCrypt extends TestCase {
String test_vectors[][] = {
{ "",
"$2a$06$DCq7YPn5Rq63x1Lad4cll.",
"$2a$06$DCq7YPn5Rq63x1Lad4cll.TV4S6ytwfsfvkgY8jIucDrjc8deX1s." },
{ "",
"$2a$08$HqWuK6/Ng6sg9gQzbLrgb.",
"$2a$08$HqWuK6/Ng6sg9gQzbLrgb.Tl.ZHfXLhvt/SgVyWhQqgqcZ7ZuUtye" },
{ "",
"$2a$10$k1wbIrmNyFAPwPVPSVa/ze",
"$2a$10$k1wbIrmNyFAPwPVPSVa/zecw2BCEnBwVS2GbrmgzxFUOqW9dk4TCW" },
{ "",
"$2a$12$k42ZFHFWqBp3vWli.nIn8u",
"$2a$12$k42ZFHFWqBp3vWli.nIn8uYyIkbvYRvodzbfbK18SSsY.CsIQPlxO" },
{ "a",
"$2a$06$m0CrhHm10qJ3lXRY.5zDGO",
"$2a$06$m0CrhHm10qJ3lXRY.5zDGO3rS2KdeeWLuGmsfGlMfOxih58VYVfxe" },
{ "a",
"$2a$08$cfcvVd2aQ8CMvoMpP2EBfe",
"$2a$08$cfcvVd2aQ8CMvoMpP2EBfeodLEkkFJ9umNEfPD18.hUF62qqlC/V." },
{ "a",
"$2a$10$k87L/MF28Q673VKh8/cPi.",
"$2a$10$k87L/MF28Q673VKh8/cPi.SUl7MU/rWuSiIDDFayrKk/1tBsSQu4u" },
{ "a",
"$2a$12$8NJH3LsPrANStV6XtBakCe",
"$2a$12$8NJH3LsPrANStV6XtBakCez0cKHXVxmvxIlcz785vxAIZrihHZpeS" },
{ "abc",
"$2a$06$If6bvum7DFjUnE9p2uDeDu",
"$2a$06$If6bvum7DFjUnE9p2uDeDu0YHzrHM6tf.iqN8.yx.jNN1ILEf7h0i" },
{ "abc",
"$2a$08$Ro0CUfOqk6cXEKf3dyaM7O",
"$2a$08$Ro0CUfOqk6cXEKf3dyaM7OhSCvnwM9s4wIX9JeLapehKK5YdLxKcm" },
{ "abc",
"$2a$10$WvvTPHKwdBJ3uk0Z37EMR.",
"$2a$10$WvvTPHKwdBJ3uk0Z37EMR.hLA2W6N9AEBhEgrAOljy2Ae5MtaSIUi" },
{ "abc",
"$2a$12$EXRkfkdmXn2gzds2SSitu.",
"$2a$12$EXRkfkdmXn2gzds2SSitu.MW9.gAVqa9eLS1//RYtYCmB1eLHg.9q" },
{ "abcdefghijklmnopqrstuvwxyz",
"$2a$06$.rCVZVOThsIa97pEDOxvGu",
"$2a$06$.rCVZVOThsIa97pEDOxvGuRRgzG64bvtJ0938xuqzv18d3ZpQhstC" },
{ "abcdefghijklmnopqrstuvwxyz",
"$2a$08$aTsUwsyowQuzRrDqFflhge",
"$2a$08$aTsUwsyowQuzRrDqFflhgekJ8d9/7Z3GV3UcgvzQW3J5zMyrTvlz." },
{ "abcdefghijklmnopqrstuvwxyz",
"$2a$10$fVH8e28OQRj9tqiDXs1e1u",
"$2a$10$fVH8e28OQRj9tqiDXs1e1uxpsjN0c7II7YPKXua2NAKYvM6iQk7dq" },
{ "abcdefghijklmnopqrstuvwxyz",
"$2a$12$D4G5f18o7aMMfwasBL7Gpu",
"$2a$12$D4G5f18o7aMMfwasBL7GpuQWuP3pkrZrOAnqP.bmezbMng.QwJ/pG" },
{ "~!@#$%^&*() ~!@#$%^&*()PNBFRD",
"$2a$06$fPIsBO8qRqkjj273rfaOI.",
"$2a$06$fPIsBO8qRqkjj273rfaOI.HtSV9jLDpTbZn782DC6/t7qT67P6FfO" },
{ "~!@#$%^&*() ~!@#$%^&*()PNBFRD",
"$2a$08$Eq2r4G/76Wv39MzSX262hu",
"$2a$08$Eq2r4G/76Wv39MzSX262huzPz612MZiYHVUJe/OcOql2jo4.9UxTW" },
{ "~!@#$%^&*() ~!@#$%^&*()PNBFRD",
"$2a$10$LgfYWkbzEvQ4JakH7rOvHe",
"$2a$10$LgfYWkbzEvQ4JakH7rOvHe0y8pHKF9OaFgwUZ2q7W2FFZmZzJYlfS" },
{ "~!@#$%^&*() ~!@#$%^&*()PNBFRD",
"$2a$12$WApznUOJfkEGSmYRfnkrPO",
"$2a$12$WApznUOJfkEGSmYRfnkrPOr466oFDCaj4b6HY3EXGvfxm43seyhgC" },
};
/**
* Test method for 'BCrypt.hashpw(String, String)'
*/
public void testHashpw() {
System.out.print("BCrypt.hashpw(): ");
for (int i = 0; i < test_vectors.length; i++) {
String plain = test_vectors[i][0];
String salt = test_vectors[i][1];
String expected = test_vectors[i][2];
String hashed = BCrypt.hashpw(plain, salt);
assertEquals(hashed, expected);
System.out.print(".");
}
System.out.println("");
}
/**
* Test method for 'BCrypt.gensalt(int)'
*/
public void testGensaltInt() {
System.out.print("BCrypt.gensalt(log_rounds):");
for (int i = 4; i <= 12; i++) {
System.out.print(" " + Integer.toString(i) + ":");
for (int j = 0; j < test_vectors.length; j += 4) {
String plain = test_vectors[j][0];
String salt = BCrypt.gensalt(i);
String hashed1 = BCrypt.hashpw(plain, salt);
String hashed2 = BCrypt.hashpw(plain, hashed1);
assertEquals(hashed1, hashed2);
System.out.print(".");
}
}
System.out.println("");
}
/**
* Test method for 'BCrypt.gensalt()'
*/
public void testGensalt() {
System.out.print("BCrypt.gensalt(): ");
for (int i = 0; i < test_vectors.length; i += 4) {
String plain = test_vectors[i][0];
String salt = BCrypt.gensalt();
String hashed1 = BCrypt.hashpw(plain, salt);
String hashed2 = BCrypt.hashpw(plain, hashed1);
assertEquals(hashed1, hashed2);
System.out.print(".");
}
System.out.println("");
}
/**
* Test method for 'BCrypt.checkpw(String, String)'
* expecting success
*/
public void testCheckpw_success() {
System.out.print("BCrypt.checkpw w/ good passwords: ");
for (int i = 0; i < test_vectors.length; i++) {
String plain = test_vectors[i][0];
String expected = test_vectors[i][2];
assertTrue(BCrypt.checkpw(plain, expected));
System.out.print(".");
}
System.out.println("");
}
/**
* Test method for 'BCrypt.checkpw(String, String)'
* expecting failure
*/
public void testCheckpw_failure() {
System.out.print("BCrypt.checkpw w/ bad passwords: ");
for (int i = 0; i < test_vectors.length; i++) {
int broken_index = (i + 4) % test_vectors.length;
String plain = test_vectors[i][0];
String expected = test_vectors[broken_index][2];
assertFalse(BCrypt.checkpw(plain, expected));
System.out.print(".");
}
System.out.println("");
}
/**
* Test for correct hashing of non-US-ASCII passwords
*/
public void testInternationalChars() {
System.out.print("BCrypt.hashpw w/ international chars: ");
String pw1 = "\u2605\u2605\u2605\u2605\u2605\u2605\u2605\u2605";
String pw2 = "????????";
String h1 = BCrypt.hashpw(pw1, BCrypt.gensalt());
assertFalse(BCrypt.checkpw(pw2, h1));
System.out.print(".");
String h2 = BCrypt.hashpw(pw2, BCrypt.gensalt());
assertFalse(BCrypt.checkpw(pw1, h2));
System.out.print(".");
System.out.println("");
}
}

View File

@ -1,170 +0,0 @@
/*
* Copyright 2015 dorkbox, llc
*
* 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.
*/
package dorkbox.util.crypto;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Calendar;
import java.util.Date;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.RSAKeyParameters;
import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
import org.junit.Test;
public class x509Test {
private static String entropySeed = "asdjhaffasdgfaasttjjhgpx600gn,-356268909087s0dfg4-42kjh255124515hasdg87";
@Test
public void EcdsaCertificate() throws IOException {
// create the certificate
Calendar expiry = Calendar.getInstance();
expiry.add(Calendar.DAY_OF_YEAR, 360);
Date startDate = new Date(); // time from which certificate is valid
Date expiryDate = expiry.getTime(); // time after which certificate is not valid
BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis()); // serial number for certificate
AsymmetricCipherKeyPair generateKeyPair = CryptoECC.generateKeyPair(CryptoECC.p521_curve, new SecureRandom()); // key name from Crypto class
ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) generateKeyPair.getPrivate();
ECPublicKeyParameters publicKey = (ECPublicKeyParameters) generateKeyPair.getPublic();
X509CertificateHolder ECDSAx509Certificate = CryptoX509.ECDSA.createCertHolder("SHA384",
startDate, expiryDate,
new X500Name("CN=Test"), new X500Name("CN=Test"), serialNumber,
privateKey, publicKey);
// make sure it's a valid cert.
if (ECDSAx509Certificate != null) {
boolean valid = CryptoX509.ECDSA.validate(ECDSAx509Certificate);
if (!valid) {
fail("Unable to verify a x509 certificate.");
}
} else {
fail("Unable to create a x509 certificate.");
}
// now sign something, then verify the signature.
byte[] data = "My keyboard is awesome".getBytes();
byte[] signatureBlock = CryptoX509.createSignature(data, ECDSAx509Certificate, privateKey);
boolean verifySignature = CryptoX509.ECDSA.verifySignature(signatureBlock, publicKey);
if (!verifySignature) {
fail("Unable to verify a x509 certificate signature.");
}
}
@Test
public void DsaCertificate() throws IOException {
// create the certificate
Calendar expiry = Calendar.getInstance();
expiry.add(Calendar.DAY_OF_YEAR, 360);
Date startDate = new Date(); // time from which certificate is valid
Date expiryDate = expiry.getTime(); // time after which certificate is not valid
BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis()); // serial number for certificate
@SuppressWarnings("deprecation")
AsymmetricCipherKeyPair generateKeyPair = CryptoDSA.generateKeyPair(new SecureRandom(entropySeed.getBytes()), 1024);
DSAPrivateKeyParameters privateKey = (DSAPrivateKeyParameters) generateKeyPair.getPrivate();
DSAPublicKeyParameters publicKey = (DSAPublicKeyParameters) generateKeyPair.getPublic();
X509CertificateHolder DSAx509Certificate = CryptoX509.DSA.createCertHolder(startDate, expiryDate,
new X500Name("CN=Test"), new X500Name("CN=Test"), serialNumber,
privateKey, publicKey);
// make sure it's a valid cert.
if (DSAx509Certificate != null) {
boolean valid = CryptoX509.DSA.validate(DSAx509Certificate);
if (!valid) {
fail("Unable to verify a x509 certificate.");
}
} else {
fail("Unable to create a x509 certificate.");
}
// now sign something, then verify the signature.
byte[] data = "My keyboard is awesome".getBytes();
byte[] signatureBlock = CryptoX509.createSignature(data, DSAx509Certificate, privateKey);
boolean verifySignature = CryptoX509.DSA.verifySignature(signatureBlock, publicKey);
if (!verifySignature) {
fail("Unable to verify a x509 certificate signature.");
}
}
@Test
public void RsaCertificate() throws IOException {
// create the certificate
Calendar expiry = Calendar.getInstance();
expiry.add(Calendar.DAY_OF_YEAR, 360);
Date startDate = new Date(); // time from which certificate is valid
Date expiryDate = expiry.getTime(); // time after which certificate is not valid
BigInteger serialNumber = BigInteger.valueOf(System.currentTimeMillis()); // serial number for certificate
@SuppressWarnings("deprecation")
AsymmetricCipherKeyPair generateKeyPair = CryptoRSA.generateKeyPair(new SecureRandom(entropySeed.getBytes()), 1024);
RSAPrivateCrtKeyParameters privateKey = (RSAPrivateCrtKeyParameters) generateKeyPair.getPrivate();
RSAKeyParameters publicKey = (RSAKeyParameters) generateKeyPair.getPublic();
X509CertificateHolder RSAx509Certificate = CryptoX509.RSA.createCertHolder(startDate, expiryDate,
new X500Name("CN=Test"), new X500Name("CN=Test"), serialNumber,
privateKey, publicKey);
// make sure it's a valid cert.
if (RSAx509Certificate != null) {
boolean valid = CryptoX509.RSA.validate(RSAx509Certificate);
if (!valid) {
fail("Unable to verify a x509 certificate.");
}
} else {
fail("Unable to create a x509 certificate.");
}
// now sign something, then verify the signature.
byte[] data = "My keyboard is awesome".getBytes();
byte[] signatureBlock = CryptoX509.createSignature(data, RSAx509Certificate, privateKey);
boolean verifySignature = CryptoX509.RSA.verifySignature(signatureBlock, publicKey);
if (!verifySignature) {
fail("Unable to verify a x509 certificate signature.");
}
}
}