Skip to content

Commit

Permalink
Add BLAKE2 Keyed Hashing (MAC)
Browse files Browse the repository at this point in the history
  • Loading branch information
05nelsonm committed Jan 13, 2025
1 parent dacae80 commit f3b330d
Show file tree
Hide file tree
Showing 44 changed files with 1,511 additions and 13 deletions.
2 changes: 2 additions & 0 deletions benchmarks/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,13 @@ kmpConfiguration {
implementation(libs.benchmark.runtime)
implementation(libs.kotlincrypto.bitops.bits)
implementation(libs.kotlincrypto.bitops.endian)
implementation(libs.kotlincrypto.hash.blake2)
implementation(libs.kotlincrypto.hash.md)
implementation(libs.kotlincrypto.hash.sha1)
implementation(libs.kotlincrypto.hash.sha2)
implementation(libs.kotlincrypto.hash.sha3)
implementation(libs.kotlincrypto.sponges.keccak)
implementation(project(":library:blake2"))
implementation(project(":library:hmac:hmac-md"))
implementation(project(":library:hmac:hmac-sha1"))
implementation(project(":library:hmac:hmac-sha2"))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2024 Matthew Nelson
*
* 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
*
* https://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.
**/
@file:Suppress("SpellCheckingInspection", "unused", "ClassName")

package org.kotlincrypto.macs.benchmarks

import kotlinx.benchmark.*
import org.kotlincrypto.core.mac.Mac
import org.kotlincrypto.macs.blake2.BLAKE2b
import org.kotlincrypto.macs.blake2.BLAKE2s

@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(BenchmarkTimeUnit.NANOSECONDS)
@Warmup(iterations = ITERATIONS, time = TIME_WARMUP)
@Measurement(iterations = ITERATIONS, time = TIME_MEASURE)
open class BLAKE2b_512Benchmark: MacBenchmarkBase() {
override val m: Mac = BLAKE2b(key, 512)
}

@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(BenchmarkTimeUnit.NANOSECONDS)
@Warmup(iterations = ITERATIONS, time = TIME_WARMUP)
@Measurement(iterations = ITERATIONS, time = TIME_MEASURE)
open class BLAKE2s_256Benchmark: MacBenchmarkBase(keyLength = 30) {
override val m: Mac = BLAKE2s(key, 256)
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ const val ITERATIONS = 5
const val TIME_WARMUP = 2
const val TIME_MEASURE = 4

abstract class MacBenchmarkBase {
abstract class MacBenchmarkBase(keyLength: Int = 50) {

protected val key = Random.Default.nextBytes(50)
protected val key = Random.Default.nextBytes(keyLength)

protected abstract val m: Mac
private val bytes by lazy { Random.Default.nextBytes((m.macLength() * 2) + 10) }
Expand Down
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ gradle-publish-maven = { module = "com.vanniktech:gradle-maven-publish-plugin",
kotlincrypto-core-digest = { module = "org.kotlincrypto.core:digest", version.ref = "kotlincrypto-core" }
kotlincrypto-core-mac = { module = "org.kotlincrypto.core:mac", version.ref = "kotlincrypto-core" }
kotlincrypto-core-xof = { module = "org.kotlincrypto.core:xof", version.ref = "kotlincrypto-core" }
kotlincrypto-hash-blake2 = { module = "org.kotlincrypto.hash:blake2", version.ref = "kotlincrypto-hash" }
kotlincrypto-hash-md = { module = "org.kotlincrypto.hash:md", version.ref = "kotlincrypto-hash" }
kotlincrypto-hash-sha1 = { module = "org.kotlincrypto.hash:sha1", version.ref = "kotlincrypto-hash" }
kotlincrypto-hash-sha2 = { module = "org.kotlincrypto.hash:sha2", version.ref = "kotlincrypto-hash" }
Expand Down
1 change: 1 addition & 0 deletions library/blake2/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
23 changes: 23 additions & 0 deletions library/blake2/api/blake2.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
public abstract class org/kotlincrypto/macs/blake2/BLAKE2Mac : org/kotlincrypto/core/mac/Mac {
public synthetic fun <init> (Lorg/kotlincrypto/macs/blake2/BLAKE2Mac$Engine;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (Lorg/kotlincrypto/macs/blake2/BLAKE2Mac;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> ([BI[BLorg/kotlincrypto/hash/blake2/BLAKE2Digest$KeyedHashFactory;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public abstract fun copy ()Lorg/kotlincrypto/macs/blake2/BLAKE2Mac;
}

public final class org/kotlincrypto/macs/blake2/BLAKE2b : org/kotlincrypto/macs/blake2/BLAKE2Mac {
public fun <init> ([BI)V
public fun <init> ([BI[B)V
public synthetic fun copy ()Ljava/lang/Object;
public synthetic fun copy ()Lorg/kotlincrypto/macs/blake2/BLAKE2Mac;
public fun copy ()Lorg/kotlincrypto/macs/blake2/BLAKE2b;
}

public final class org/kotlincrypto/macs/blake2/BLAKE2s : org/kotlincrypto/macs/blake2/BLAKE2Mac {
public fun <init> ([BI)V
public fun <init> ([BI[B)V
public synthetic fun copy ()Ljava/lang/Object;
public synthetic fun copy ()Lorg/kotlincrypto/macs/blake2/BLAKE2Mac;
public fun copy ()Lorg/kotlincrypto/macs/blake2/BLAKE2s;
}

28 changes: 28 additions & 0 deletions library/blake2/api/blake2.klib.api
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Klib ABI Dump
// Targets: [androidNativeArm32, androidNativeArm64, androidNativeX64, androidNativeX86, iosArm64, iosSimulatorArm64, iosX64, js, linuxArm64, linuxX64, macosArm64, macosX64, mingwX64, tvosArm64, tvosSimulatorArm64, tvosX64, wasmJs, wasmWasi, watchosArm32, watchosArm64, watchosDeviceArm64, watchosSimulatorArm64, watchosX64]
// Rendering settings:
// - Signature version: 2
// - Show manifest properties: true
// - Show declarations: true

// Library unique name: <org.kotlincrypto.macs:blake2>
final class org.kotlincrypto.macs.blake2/BLAKE2b : org.kotlincrypto.macs.blake2/BLAKE2Mac { // org.kotlincrypto.macs.blake2/BLAKE2b|null[0]
constructor <init>(kotlin/ByteArray, kotlin/Int) // org.kotlincrypto.macs.blake2/BLAKE2b.<init>|<init>(kotlin.ByteArray;kotlin.Int){}[0]
constructor <init>(kotlin/ByteArray, kotlin/Int, kotlin/ByteArray?) // org.kotlincrypto.macs.blake2/BLAKE2b.<init>|<init>(kotlin.ByteArray;kotlin.Int;kotlin.ByteArray?){}[0]

final fun copy(): org.kotlincrypto.macs.blake2/BLAKE2b // org.kotlincrypto.macs.blake2/BLAKE2b.copy|copy(){}[0]
}

final class org.kotlincrypto.macs.blake2/BLAKE2s : org.kotlincrypto.macs.blake2/BLAKE2Mac { // org.kotlincrypto.macs.blake2/BLAKE2s|null[0]
constructor <init>(kotlin/ByteArray, kotlin/Int) // org.kotlincrypto.macs.blake2/BLAKE2s.<init>|<init>(kotlin.ByteArray;kotlin.Int){}[0]
constructor <init>(kotlin/ByteArray, kotlin/Int, kotlin/ByteArray?) // org.kotlincrypto.macs.blake2/BLAKE2s.<init>|<init>(kotlin.ByteArray;kotlin.Int;kotlin.ByteArray?){}[0]

final fun copy(): org.kotlincrypto.macs.blake2/BLAKE2s // org.kotlincrypto.macs.blake2/BLAKE2s.copy|copy(){}[0]
}

sealed class org.kotlincrypto.macs.blake2/BLAKE2Mac : org.kotlincrypto.core.mac/Mac { // org.kotlincrypto.macs.blake2/BLAKE2Mac|null[0]
constructor <init>(kotlin/ByteArray, kotlin/Int, kotlin/ByteArray?, org.kotlincrypto.hash.blake2/BLAKE2Digest.KeyedHashFactory<*>) // org.kotlincrypto.macs.blake2/BLAKE2Mac.<init>|<init>(kotlin.ByteArray;kotlin.Int;kotlin.ByteArray?;org.kotlincrypto.hash.blake2.BLAKE2Digest.KeyedHashFactory<*>){}[0]
constructor <init>(org.kotlincrypto.macs.blake2/BLAKE2Mac) // org.kotlincrypto.macs.blake2/BLAKE2Mac.<init>|<init>(org.kotlincrypto.macs.blake2.BLAKE2Mac){}[0]

abstract fun copy(): org.kotlincrypto.macs.blake2/BLAKE2Mac // org.kotlincrypto.macs.blake2/BLAKE2Mac.copy|copy(){}[0]
}
37 changes: 37 additions & 0 deletions library/blake2/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright (c) 2025 Matthew Nelson
*
* 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
*
* https://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.
**/
plugins {
id("configuration")
id("bom-include")
}

kmpConfiguration {
configureShared(java9ModuleName = "org.kotlincrypto.macs.blake2", publish = true) {
common {
sourceSetMain {
dependencies {
implementation(libs.kotlincrypto.hash.blake2)
api(libs.kotlincrypto.core.mac)
}
}
sourceSetTest {
dependencies {
implementation(project(":tools:testing"))
}
}
}
}
}
16 changes: 16 additions & 0 deletions library/blake2/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright (c) 2025 Matthew Nelson
#
# 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
#
# https://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.
POM_ARTIFACT_ID=blake2
POM_NAME=KotlinCrypto MACs BLAKE2b, BLAKE2s
POM_DESCRIPTION=MACs for BLAKE2b, BLAKE2s
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* Copyright (c) 2025 Matthew Nelson
*
* 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package org.kotlincrypto.macs.blake2

import org.kotlincrypto.core.Algorithm
import org.kotlincrypto.core.InternalKotlinCryptoApi
import org.kotlincrypto.core.mac.Mac
import org.kotlincrypto.hash.blake2.BLAKE2Digest

/**
* Core abstraction for BLAKE2 Keyed Hashing in the form of Message Authentication
*
* Code implementations:
* - [BLAKE2b]
* - [BLAKE2s]
* */
public sealed class BLAKE2Mac: Mac {

@Throws(IllegalArgumentException::class)
protected constructor(
key: ByteArray,
bitStrength: Int,
personalization: ByteArray?,
factory: BLAKE2Digest.KeyedHashFactory<*>,
): this(Engine(key, bitStrength, personalization, factory))

protected constructor(other: BLAKE2Mac): super(other)

private constructor(engine: Engine): super(engine.algorithm(), engine)

public abstract override fun copy(): BLAKE2Mac

@OptIn(InternalKotlinCryptoApi::class)
private class Engine: Mac.Engine, Algorithm {

private val bitStrength: Int
private val keyBlock: ByteArray
private val personalization: ByteArray?
private val factory: BLAKE2Digest.KeyedHashFactory<*>
private var digest: BLAKE2Digest

@Throws(IllegalArgumentException::class)
constructor(
key: ByteArray,
bitStrength: Int,
personalization: ByteArray?,
factory: BLAKE2Digest.KeyedHashFactory<*>,
): super(key) {
this.bitStrength = bitStrength
this.personalization = personalization?.copyOf()
this.digest = factory.keyedHashInstance(
bitStrength = bitStrength,
keyLength = key.size,
salt = null,
personalization = this.personalization,
)
this.factory = factory
this.keyBlock = key.copyOf(this.digest.blockSize())
this.digest.update(this.keyBlock)
}

private constructor(other: Engine): super(other) {
this.bitStrength = other.bitStrength
this.keyBlock = other.keyBlock.copyOf()
this.personalization = other.personalization
this.factory = other.factory
this.digest = other.digest.copy()
}

override fun copy(): Engine = Engine(this)

override fun update(input: Byte) { digest.update(input) }

override fun update(input: ByteArray, offset: Int, len: Int) { digest.update(input, offset, len) }

override fun doFinal(): ByteArray = digest.digest()

override fun reset() {
digest.reset()
digest.update(keyBlock)
}

override fun reset(newKey: ByteArray) {
val oldDigest = digest
this.digest = factory.keyedHashInstance(
bitStrength = bitStrength,
keyLength = newKey.size,
salt = null,
personalization = personalization,
)

newKey.copyInto(keyBlock)
keyBlock.fill(0, newKey.size)
digest.update(keyBlock)
oldDigest.reset()
}

override fun algorithm(): String = digest.algorithm()

override fun macLength(): Int = digest.digestLength()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2025 Matthew Nelson
*
* 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
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package org.kotlincrypto.macs.blake2

/**
* BLAKE2b Keyed Hashing implementation
*
* https://datatracker.ietf.org/doc/rfc7693/
* */
public class BLAKE2b: BLAKE2Mac {

/**
* Creates a new instance of [BLAKE2b].
*
* @param [key] The secret to use for authenticating data.
* @param [bitStrength] The number of bits returned when [doFinal] is invoked.
*
* @throws [IllegalArgumentException] when:
* - [key] size is less than 1
* - [key] size is greater than 64
* - [bitStrength] is less than 8
* - [bitStrength] is greater than 512
* - [bitStrength] is not a factor of 8
* */
public constructor(
key: ByteArray,
bitStrength: Int,
): this(key, bitStrength, null)

/**
* Creates a new instance of [BLAKE2b].
*
* @param [key] The secret to use for authenticating data.
* @param [bitStrength] The number of bits returned when [doFinal] is invoked.
* @param [personalization] A user selected customization bit string to define a variant
* of the function. When no customization is desired, [personalization] must be set to a
* null value. Must be 16 bytes in length, or null.
*
* @throws [IllegalArgumentException] when:
* - [key] size is less than 1
* - [key] size is greater than 64
* - [bitStrength] is less than 8
* - [bitStrength] is greater than 512
* - [bitStrength] is not a factor of 8
* - [personalization] is non-null and not exactly 16 bytes in length
* */
public constructor(
key: ByteArray,
bitStrength: Int,
personalization: ByteArray?,
): super(
key = key,
bitStrength = bitStrength,
personalization = personalization,
factory = org.kotlincrypto.hash.blake2.BLAKE2b,
)

private constructor(other: BLAKE2b): super(other)

public override fun copy(): BLAKE2b = BLAKE2b(this)
}
Loading

0 comments on commit f3b330d

Please sign in to comment.