diff options
Diffstat (limited to 'okio/src/jvmMain/kotlin/okio/HashingSink.kt')
-rw-r--r-- | okio/src/jvmMain/kotlin/okio/HashingSink.kt | 139 |
1 files changed, 139 insertions, 0 deletions
diff --git a/okio/src/jvmMain/kotlin/okio/HashingSink.kt b/okio/src/jvmMain/kotlin/okio/HashingSink.kt new file mode 100644 index 00000000..36bfd2b8 --- /dev/null +++ b/okio/src/jvmMain/kotlin/okio/HashingSink.kt @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2016 Square, Inc. + * + * 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 okio + +import java.io.IOException +import java.security.InvalidKeyException +import java.security.MessageDigest +import javax.crypto.Mac +import javax.crypto.spec.SecretKeySpec + +/** + * A sink that computes a hash of the full stream of bytes it has accepted. To use, create an + * instance with your preferred hash algorithm. Write all of the data to the sink and then call + * [hash] to compute the final hash value. + * + * In this example we use `HashingSink` with a [BufferedSink] to make writing to the + * sink easier. + * ``` + * HashingSink hashingSink = HashingSink.sha256(s); + * BufferedSink bufferedSink = Okio.buffer(hashingSink); + * + * ... // Write to bufferedSink and either flush or close it. + * + * ByteString hash = hashingSink.hash(); + * ``` + */ +actual class HashingSink : ForwardingSink, Sink { // Need to explicitly declare sink pending fix for https://youtrack.jetbrains.com/issue/KT-20641 + private val messageDigest: MessageDigest? + private val mac: Mac? + + internal constructor(sink: Sink, digest: MessageDigest) : super(sink) { + this.messageDigest = digest + this.mac = null + } + + internal constructor(sink: Sink, algorithm: String) : this(sink, MessageDigest.getInstance(algorithm)) + + internal constructor(sink: Sink, mac: Mac) : super(sink) { + this.mac = mac + this.messageDigest = null + } + + internal constructor(sink: Sink, key: ByteString, algorithm: String) : this( + sink, + try { + Mac.getInstance(algorithm).apply { + init(SecretKeySpec(key.toByteArray(), algorithm)) + } + } catch (e: InvalidKeyException) { + throw IllegalArgumentException(e) + } + ) + + @Throws(IOException::class) + override fun write(source: Buffer, byteCount: Long) { + checkOffsetAndCount(source.size, 0, byteCount) + + // Hash byteCount bytes from the prefix of source. + var hashedCount = 0L + var s = source.head!! + while (hashedCount < byteCount) { + val toHash = minOf(byteCount - hashedCount, s.limit - s.pos).toInt() + if (messageDigest != null) { + messageDigest.update(s.data, s.pos, toHash) + } else { + mac!!.update(s.data, s.pos, toHash) + } + hashedCount += toHash + s = s.next!! + } + + // Write those bytes to the sink. + super.write(source, byteCount) + } + + /** + * Returns the hash of the bytes accepted thus far and resets the internal state of this sink. + * + * **Warning:** This method is not idempotent. Each time this method is called its + * internal state is cleared. This starts a new hash with zero bytes accepted. + */ + @get:JvmName("hash") + actual val hash: ByteString + get() { + val result = if (messageDigest != null) messageDigest.digest() else mac!!.doFinal() + return ByteString(result) + } + + @JvmName("-deprecated_hash") + @Deprecated( + message = "moved to val", + replaceWith = ReplaceWith(expression = "hash"), + level = DeprecationLevel.ERROR + ) + fun hash() = hash + + actual companion object { + /** Returns a sink that uses the obsolete MD5 hash algorithm to produce 128-bit hashes. */ + @JvmStatic + actual fun md5(sink: Sink) = HashingSink(sink, "MD5") + + /** Returns a sink that uses the obsolete SHA-1 hash algorithm to produce 160-bit hashes. */ + @JvmStatic + actual fun sha1(sink: Sink) = HashingSink(sink, "SHA-1") + + /** Returns a sink that uses the SHA-256 hash algorithm to produce 256-bit hashes. */ + @JvmStatic + actual fun sha256(sink: Sink) = HashingSink(sink, "SHA-256") + + /** Returns a sink that uses the SHA-512 hash algorithm to produce 512-bit hashes. */ + @JvmStatic + actual fun sha512(sink: Sink) = HashingSink(sink, "SHA-512") + + /** Returns a sink that uses the obsolete SHA-1 HMAC algorithm to produce 160-bit hashes. */ + @JvmStatic + actual fun hmacSha1(sink: Sink, key: ByteString) = HashingSink(sink, key, "HmacSHA1") + + /** Returns a sink that uses the SHA-256 HMAC algorithm to produce 256-bit hashes. */ + @JvmStatic + actual fun hmacSha256(sink: Sink, key: ByteString) = HashingSink(sink, key, "HmacSHA256") + + /** Returns a sink that uses the SHA-512 HMAC algorithm to produce 512-bit hashes. */ + @JvmStatic + actual fun hmacSha512(sink: Sink, key: ByteString) = HashingSink(sink, key, "HmacSHA512") + } +} |