Added Hashing for InputStreams

This commit is contained in:
Robinson 2023-07-13 23:18:07 +02:00
parent bdff9f7282
commit 72a1b21f3f
No known key found for this signature in database
GPG Key ID: 8E7DB78588BD6F5C
1 changed files with 122 additions and 26 deletions

View File

@ -15,10 +15,12 @@
*/ */
package dorkbox.bytes package dorkbox.bytes
import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil.close
import net.jpountz.xxhash.StreamingXXHash32 import net.jpountz.xxhash.StreamingXXHash32
import net.jpountz.xxhash.StreamingXXHash64 import net.jpountz.xxhash.StreamingXXHash64
import net.jpountz.xxhash.XXHashFactory import net.jpountz.xxhash.XXHashFactory
import java.io.File import java.io.File
import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.security.MessageDigest import java.security.MessageDigest
import java.security.NoSuchAlgorithmException import java.security.NoSuchAlgorithmException
@ -236,6 +238,44 @@ private fun hash(byteArray: ByteArray, start: Int, length: Int, digest: MessageD
return digest.digest() return digest.digest()
} }
private fun hash(string: String, start: Int, length: Int, digest: MessageDigest): ByteArray {
val charToBytes = string.toBytes16(start, length)
digest.reset()
digest.update(charToBytes, 0, charToBytes.size)
return digest.digest()
}
private fun hash(inputStream: InputStream, bufferSize: Int = 4096, digest: MessageDigest): ByteArray {
val buffer = ByteArray(bufferSize)
var read: Int
digest.reset()
inputStream.use {
while (it.read(buffer).also { read = it } > 0) {
digest.update(buffer, 0, read)
}
}
return digest.digest()
}
private fun hash(file: File, start: Long, length: Long, bufferSize: Int, digest: MessageDigest): ByteArray {
require(file.isFile) { "Unable open as file: ${file.absolutePath}" }
require(file.canRead()) { "Unable to read file: ${file.absolutePath}" }
require(start >= 0) { "Start ($start) must be >= 0" }
require(length >= 0) { "Length ($length) must be >= 0" }
require(start < file.length()) { "Start ($start) position must be smaller than the size of the file" }
file.inputStream().use {
digest.reset()
updateDigest(digest, it, bufferSize, start, length)
return digest.digest()
}
}
@Deprecated("Do not use this, it is insecure and prone to attack!") @Deprecated("Do not use this, it is insecure and prone to attack!")
fun ByteArray.md5(start: Int = 0, length: Int = this.size): ByteArray = hash(this, start, length, Hash.digestMd5.get()) fun ByteArray.md5(start: Int = 0, length: Int = this.size): ByteArray = hash(this, start, length, Hash.digestMd5.get())
fun ByteArray.sha1(start: Int = 0, length: Int = this.size): ByteArray = hash(this, start, length, Hash.digest1.get()) fun ByteArray.sha1(start: Int = 0, length: Int = this.size): ByteArray = hash(this, start, length, Hash.digest1.get())
@ -245,6 +285,7 @@ fun ByteArray.sha512(start: Int = 0, length: Int = this.size): ByteArray = hash(
fun ByteArray.sha3_256(start: Int = 0, length: Int = this.size): ByteArray = hash(this, start, length, Hash.digest3_256.get()) fun ByteArray.sha3_256(start: Int = 0, length: Int = this.size): ByteArray = hash(this, start, length, Hash.digest3_256.get())
fun ByteArray.sha3_384(start: Int = 0, length: Int = this.size): ByteArray = hash(this, start, length, Hash.digest3_384.get()) fun ByteArray.sha3_384(start: Int = 0, length: Int = this.size): ByteArray = hash(this, start, length, Hash.digest3_384.get())
fun ByteArray.sha3_512(start: Int = 0, length: Int = this.size): ByteArray = hash(this, start, length, Hash.digest3_512.get()) fun ByteArray.sha3_512(start: Int = 0, length: Int = this.size): ByteArray = hash(this, start, length, Hash.digest3_512.get())
/** /**
* @param seed used to initialize the hash value (for the xxhash seed), use whatever value you want, but always the same * @param seed used to initialize the hash value (for the xxhash seed), use whatever value you want, but always the same
*/ */
@ -255,6 +296,7 @@ fun ByteArray.xxHash32(seed: Int = -0x31bf6a3c, start: Int = 0, length: Int = th
hash32.update(this, start, length) hash32.update(this, start, length)
return hash32.value return hash32.value
} }
/** /**
* @param seed used to initialize the hash value (for the xxhash seed), use whatever value you want, but always the same * @param seed used to initialize the hash value (for the xxhash seed), use whatever value you want, but always the same
*/ */
@ -267,13 +309,6 @@ fun ByteArray.xxHash64(seed: Long = -0x31bf6a3c, start: Int = 0, length: Int = t
} }
private fun hash(string: String, start: Int, length: Int, digest: MessageDigest): ByteArray {
val charToBytes = string.toCharArray().toBytes16(start, length)
digest.reset()
digest.update(charToBytes, 0, charToBytes.size)
return digest.digest()
}
/** /**
* gets the MD5 hash of the specified string, as UTF-16 * gets the MD5 hash of the specified string, as UTF-16
*/ */
@ -307,6 +342,7 @@ fun String.sha3_384(start: Int = 0, length: Int = this.length): ByteArray = hash
* gets the SHA3_512 hash of the specified string, as UTF-16 * gets the SHA3_512 hash of the specified string, as UTF-16
*/ */
fun String.sha3_512(start: Int = 0, length: Int = this.length): ByteArray = hash(this, start, length, Hash.digest3_512.get()) fun String.sha3_512(start: Int = 0, length: Int = this.length): ByteArray = hash(this, start, length, Hash.digest3_512.get())
/** /**
* gets the xxHash32 of the string, as UTF-16 * gets the xxHash32 of the string, as UTF-16
* *
@ -320,6 +356,7 @@ fun String.xxHash32(seed: Int = -0x31bf6a3c, start: Int = 0, length: Int = this.
hash32.update(charToBytes, 0, charToBytes.size) hash32.update(charToBytes, 0, charToBytes.size)
return hash32.value return hash32.value
} }
/** /**
* gets the xxHash64 of the string, as UTF-16 * gets the xxHash64 of the string, as UTF-16
* *
@ -384,6 +421,7 @@ fun String.sha3_384WithSalt(saltBytes: ByteArray, start: Int = 0, length: Int =
*/ */
fun String.sha3_512WithSalt(saltBytes: ByteArray, start: Int = 0, length: Int = this.length + saltBytes.size): ByteArray = fun String.sha3_512WithSalt(saltBytes: ByteArray, start: Int = 0, length: Int = this.length + saltBytes.size): ByteArray =
hashWithSalt(this, saltBytes, start, length, Hash.digest3_512.get()) hashWithSalt(this, saltBytes, start, length, Hash.digest3_512.get())
/** /**
* gets the xxHash32 + SALT of the string, as UTF-16 * gets the xxHash32 + SALT of the string, as UTF-16
* *
@ -403,6 +441,7 @@ fun String.xxHash32WithSalt(saltBytes: ByteArray, seed: Int = -0x31bf6a3c, start
hash32.update(saltBytes, 0, saltBytes.size) hash32.update(saltBytes, 0, saltBytes.size)
return hash32.value return hash32.value
} }
/** /**
* gets the xxHash64 + SALT of the string, as UTF-16 * gets the xxHash64 + SALT of the string, as UTF-16
* *
@ -471,6 +510,7 @@ fun ByteArray.sha3_384WithSalt(saltBytes: ByteArray, start: Int = 0, length: Int
*/ */
fun ByteArray.sha3_512WithSalt(saltBytes: ByteArray, start: Int = 0, length: Int = this.size + saltBytes.size): ByteArray = fun ByteArray.sha3_512WithSalt(saltBytes: ByteArray, start: Int = 0, length: Int = this.size + saltBytes.size): ByteArray =
hashWithSalt(this, saltBytes, start, length, Hash.digest3_512.get()) hashWithSalt(this, saltBytes, start, length, Hash.digest3_512.get())
/** /**
* gets the xxHash32 + SALT of the byte array * gets the xxHash32 + SALT of the byte array
* *
@ -484,6 +524,7 @@ fun ByteArray.xxHash32WithSalt(saltBytes: ByteArray, seed: Int = -0x31bf6a3c, st
hash32.update(saltBytes, 0, saltBytes.size) hash32.update(saltBytes, 0, saltBytes.size)
return hash32.value return hash32.value
} }
/** /**
* gets the xxHash64 + SALT of the byte array * gets the xxHash64 + SALT of the byte array
* *
@ -499,25 +540,6 @@ fun ByteArray.xxHash64WithSalt(saltBytes: ByteArray, seed: Long = -0x31bf6a3c, s
} }
private fun hash(file: File, start: Long, length: Long, bufferSize: Int, digest: MessageDigest): ByteArray {
require(file.isFile) { "Unable open as file: ${file.absolutePath}" }
require(file.canRead()) { "Unable to read file: ${file.absolutePath}" }
require(start >= 0) { "Start ($start) must be >= 0" }
require(length >= 0) { "Length ($length) must be >= 0" }
require(start < file.length()) { "Start ($start) position must be smaller than the size of the file" }
file.inputStream().use {
digest.reset()
updateDigest(digest, it, bufferSize, start, length)
return digest.digest()
}
}
/** /**
* gets the SHA1 hash of the file * gets the SHA1 hash of the file
*/ */
@ -597,3 +619,77 @@ fun File.xxHash64(start: Long = 0L, length: Long = this.length(), bufferSize: In
return hash64.value return hash64.value
} }
} }
/**
* gets the SHA1 hash of the input stream
*/
fun InputStream.sha1(bufferSize: Int = 4096): ByteArray = hash(this, bufferSize, Hash.digest1.get())
/**
* gets the SHA256 hash of the file
*/
fun InputStream.sha256(bufferSize: Int = 4096): ByteArray = hash(this, bufferSize, Hash.digest256.get())
/**
* gets the SHA384 hash of the file
*/
fun InputStream.sha384(bufferSize: Int = 4096): ByteArray = hash(this, bufferSize, Hash.digest384.get())
/**
* gets the SHA512 hash of the file
*/
fun InputStream.sha512(bufferSize: Int = 4096): ByteArray = hash(this, bufferSize, Hash.digest512.get())
/**
* gets the SHA3_256 hash of the file
*/
fun InputStream.sha3_256(bufferSize: Int = 4096): ByteArray = hash(this, bufferSize, Hash.digest3_256.get())
/**
* gets the SHA3_384 hash of the file
*/
fun InputStream.sha3_384(bufferSize: Int = 4096): ByteArray = hash(this, bufferSize, Hash.digest3_384.get())
/**
* gets the SHA3_512 hash of the file
*/
fun InputStream.sha3_512(bufferSize: Int = 4096): ByteArray = hash(this, bufferSize, Hash.digest3_512.get())
/**
* Return the xxhash32 of the InputStream
*
* @param seed used to initialize the hash value (for the xxhash seed), use whatever value you want, but always the same
*/
fun InputStream.xxHash32(bufferSize: Int = 4096, seed: Int = -0x31bf6a3c): Int {
val xxHash = Hash.xxHashFactory.get()
val hash32 = xxHash.newStreamingHash32(seed)!!
val buffer = ByteArray(bufferSize)
var read: Int
this.use {
while (it.read(buffer).also { read = it } > 0) {
hash32.update(buffer, 0, read)
}
}
return hash32.value
}
/**
* Return the xxhash64 of the InputStream
*
* @param seed used to initialize the hash value (for the xxhash seed), use whatever value you want, but always the same
*/
fun InputStream.xxHash64(bufferSize: Int = 4096, seed: Long = -0x31bf6a3c): Long {
val xxHash = Hash.xxHashFactory.get()
val hash64 = xxHash.newStreamingHash64(seed)!!
val buffer = ByteArray(bufferSize)
var read: Int
this.use {
while (it.read(buffer).also { read = it } > 0) {
hash64.update(buffer, 0, read)
}
}
return hash64.value
}