Added Hashing for InputStreams

master
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
import com.sun.xml.internal.ws.streaming.XMLStreamReaderUtil.close
import net.jpountz.xxhash.StreamingXXHash32
import net.jpountz.xxhash.StreamingXXHash64
import net.jpountz.xxhash.XXHashFactory
import java.io.File
import java.io.IOException
import java.io.InputStream
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
@ -236,6 +238,44 @@ private fun hash(byteArray: ByteArray, start: Int, length: Int, digest: MessageD
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!")
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())
@ -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_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())
/**
* @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)
return hash32.value
}
/**
* @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
*/
@ -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
*/
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
*
@ -320,6 +356,7 @@ fun String.xxHash32(seed: Int = -0x31bf6a3c, start: Int = 0, length: Int = this.
hash32.update(charToBytes, 0, charToBytes.size)
return hash32.value
}
/**
* 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 =
hashWithSalt(this, saltBytes, start, length, Hash.digest3_512.get())
/**
* 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)
return hash32.value
}
/**
* 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 =
hashWithSalt(this, saltBytes, start, length, Hash.digest3_512.get())
/**
* 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)
return hash32.value
}
/**
* 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
*/
@ -597,3 +619,77 @@ fun File.xxHash64(start: Long = 0L, length: Long = this.length(), bufferSize: In
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
}